Skip to content

Commit

Permalink
Merge branch 'master' into Deon-Branch
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielEmmanuel1 authored Dec 29, 2024
2 parents 70e7519 + d41f8ec commit 4c7f835
Show file tree
Hide file tree
Showing 15 changed files with 408 additions and 271 deletions.
5 changes: 0 additions & 5 deletions .changeset/mean-monkeys-swim.md

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@biomejs/biome": "1.9.4",
"@changesets/changelog-github": "^0.4.8",
"@changesets/cli": "^2.26.2",
"@fuels/ts-config": "0.20.0",
"@fuels/ts-config": "0.26.0",
"@jest/types": "29.6.3",
"@playwright/test": "1.46.1",
"@types/jest": "^29.5.5",
Expand Down
16 changes: 16 additions & 0 deletions packages/app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# fuels-wallet

## 0.45.0

### Minor Changes

- [#1741](https://github.com/FuelLabs/fuels-wallet/pull/1741) [`197d1758`](https://github.com/FuelLabs/fuels-wallet/commit/197d1758701392bf7ab9efc97fbed3ca8d9ac567) Thanks [@nelitow](https://github.com/nelitow)! - Update @fuels packages to implement localStorage fix.

### Patch Changes

- [#1718](https://github.com/FuelLabs/fuels-wallet/pull/1718) [`7fcfee7d`](https://github.com/FuelLabs/fuels-wallet/commit/7fcfee7d8fb53259a8abd2f3843c951fcb03e865) Thanks [@nelitow](https://github.com/nelitow)! - Update E2E setup

- [#1742](https://github.com/FuelLabs/fuels-wallet/pull/1742) [`4bd6e86b`](https://github.com/FuelLabs/fuels-wallet/commit/4bd6e86b370d17692da72ff91c2b28cb64a21c2c) Thanks [@LuizAsFight](https://github.com/LuizAsFight)! - feat: add OPFS backup

- Updated dependencies [[`197d1758`](https://github.com/FuelLabs/fuels-wallet/commit/197d1758701392bf7ab9efc97fbed3ca8d9ac567)]:
- @fuels/playwright-utils@0.45.0
- @fuel-wallet/connections@0.45.0

## 0.44.1

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "fuels-wallet",
"private": true,
"version": "0.44.1",
"version": "0.45.0",
"type": "module",
"database": "23",
"scripts": {
Expand All @@ -26,7 +26,7 @@
"@fuel-ui/react": "0.23.3",
"@fuel-ui/test-utils": "0.17.0",
"@fuel-wallet/connections": "workspace:*",
"@fuels/local-storage": "0.20.0",
"@fuels/local-storage": "0.26.0",
"@fuels/playwright-utils": "workspace:*",
"@fuels/react-xstore": "0.20.0",
"@hookform/resolvers": "3.9.0",
Expand Down
178 changes: 132 additions & 46 deletions packages/app/src/systems/Account/services/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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';
import { readFromOPFS } from '~/systems/Core/utils/opfs';

export type AccountInputs = {
addAccount: {
Expand Down Expand Up @@ -215,68 +216,105 @@ export class AccountService {
allVaults,
backupNetworks,
allNetworks,
opfsBackupData,
] = await Promise.all([
chromeStorage.accounts.getAll(),
db.accounts.toArray(),
chromeStorage.vaults.getAll(),
db.vaults.toArray(),
chromeStorage.networks.getAll(),
db.networks.toArray(),
readFromOPFS(),
]);

const chromeStorageBackupData = {
accounts: backupAccounts,
vaults: backupVaults,
networks: backupNetworks,
};

// if there is no accounts, means the user lost it. try recovering it
const needsAccRecovery =
allAccounts?.length === 0 && backupAccounts?.length > 0;
allAccounts?.length === 0 &&
(chromeStorageBackupData.accounts?.length > 0 ||
opfsBackupData?.accounts?.length > 0);
const needsVaultRecovery =
allVaults?.length === 0 && backupVaults?.length > 0;
allVaults?.length === 0 &&
(chromeStorageBackupData.vaults?.length > 0 ||
opfsBackupData?.vaults?.length > 0);
const needsNetworkRecovery =
allNetworks?.length === 0 && backupNetworks?.length > 0;
allNetworks?.length === 0 &&
(chromeStorageBackupData.networks?.length > 0 ||
opfsBackupData?.networks?.length > 0);
const needsRecovery =
needsAccRecovery || needsVaultRecovery || needsNetworkRecovery;

return {
backupAccounts,
backupVaults,
backupNetworks,
needsRecovery,
needsAccRecovery,
needsVaultRecovery,
needsNetworkRecovery,
chromeStorageBackupData,
opfsBackupData,
};
}

static async recoverWallet() {
const {
backupAccounts,
backupVaults,
backupNetworks,
needsRecovery,
needsAccRecovery,
needsVaultRecovery,
needsNetworkRecovery,
} = await AccountService.fetchRecoveryState();
const { chromeStorageBackupData, needsRecovery, opfsBackupData } =
await AccountService.fetchRecoveryState();

if (needsRecovery) {
(async () => {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const dataToLog: any = {};
try {
dataToLog.backupAccounts = JSON.stringify(
backupAccounts?.map((account) => account?.data?.address) || []
);
dataToLog.backupNetworks = JSON.stringify(backupNetworks || []);
dataToLog.chromeStorageBackupData = {
...chromeStorageBackupData,
accounts:
chromeStorageBackupData.accounts?.map(
(account) => account?.data?.address
) || [],
vaults: chromeStorageBackupData.vaults?.length || 0,
};
// try getting data from indexedDB (outside of dexie) to check if it's also corrupted
const testNoDexieDbData = await getTestNoDexieDbData();
dataToLog.testNoDexieDbData = testNoDexieDbData;
} catch (_) {}
try {
dataToLog.ofpsBackupupData = {
...opfsBackupData,
accounts:
opfsBackupData.accounts?.map(
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
(account: any) => account?.address
) || [],
vaults: opfsBackupData.vaults?.length || 0,
};
} catch (_) {}

Sentry.captureException(
'Disaster on DB. Start recovering accounts / vaults / networks',
{
extra: dataToLog,
tags: { manual: true },
}
);
const hasOPFSBackup =
!!opfsBackupData?.accounts?.length ||
!!opfsBackupData?.vaults?.length ||
!!opfsBackupData?.networks?.length;
const hasChromeStorageBackup =
!!chromeStorageBackupData.accounts?.length ||
!!chromeStorageBackupData.vaults?.length ||
!!chromeStorageBackupData.networks?.length;
let sentryMsg = 'DB is cleaned. ';
if (!hasOPFSBackup && !hasChromeStorageBackup) {
sentryMsg += 'No backup found. ';
}
if (hasOPFSBackup) {
sentryMsg += 'OPFS backup is found. Recovering...';
}
if (hasChromeStorageBackup) {
sentryMsg += 'Chrome Storage backup is found. Recovering...';
}

Sentry.captureException(sentryMsg, {
extra: dataToLog,
tags: { manual: true },
});
})();

await db.transaction(
Expand All @@ -285,36 +323,84 @@ export class AccountService {
db.vaults,
db.networks,
async () => {
if (needsAccRecovery) {
let isCurrentFlag = true;
console.log('recovering accounts', backupAccounts);
for (const account of backupAccounts) {
console.log('opfsBackupData', opfsBackupData);
console.log('chromeStorageBackupData', chromeStorageBackupData);
// accounts recovery
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
async function recoverAccounts(accounts: any) {
await db.accounts.clear();
for (const account of accounts) {
// 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 (account.address) {
await db.accounts.add(account);
}
}
}
if (needsVaultRecovery) {
console.log('recovering vaults', backupVaults);
for (const vault of backupVaults) {
if (vault.key && vault.data) {
await db.vaults.add(vault.data);
// vaults recovery
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
async function recoverVaults(vaults: any) {
await db.vaults.clear();
for (const vault of vaults) {
if (vault.key) {
await db.vaults.add(vault);
}
}
}
if (needsNetworkRecovery) {
console.log('recovering networks', backupNetworks);
for (const network of backupNetworks) {
if (network.key && network.data.id) {
await db.networks.add(network.data);
// networks recovery
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
async function recoverNetworks(networks: any) {
await db.networks.clear();
for (const network of networks) {
if (network.url) {
await db.networks.add(network);
}
}
}

if (opfsBackupData?.accounts?.length) {
console.log(
'recovering accounts from OPFS',
opfsBackupData.accounts
);
await recoverAccounts(opfsBackupData.accounts);
} else if (chromeStorageBackupData.accounts?.length) {
console.log(
'recovering accounts from Chrome Storage',
chromeStorageBackupData.accounts
);
await recoverAccounts(
chromeStorageBackupData.accounts?.map((account) => account.data)
);
}

if (opfsBackupData?.vaults?.length) {
console.log('recovering vaults from OPFS', opfsBackupData.vaults);
await recoverVaults(opfsBackupData.vaults);
} else if (chromeStorageBackupData.vaults?.length) {
console.log(
'recovering vaults from Chrome Storage',
chromeStorageBackupData.vaults
);
await recoverVaults(
chromeStorageBackupData.vaults?.map((vault) => vault.data)
);
}

if (opfsBackupData?.networks?.length) {
console.log(
'recovering networks from OPFS',
opfsBackupData.networks
);
await recoverNetworks(opfsBackupData.networks);
} else if (chromeStorageBackupData.networks?.length) {
console.log(
'recovering networks from Chrome Storage',
chromeStorageBackupData.networks
);
await recoverNetworks(
chromeStorageBackupData.networks?.map((network) => network.data)
);
}
}
);
}
Expand Down
28 changes: 23 additions & 5 deletions packages/app/src/systems/Core/utils/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { applyDbVersioning } from './databaseVersioning';
import { createParallelDb } from '~/systems/Core/utils/databaseNoDexie';
import { IS_LOGGED_KEY } from '~/config';
import { Storage } from '~/systems/Core/utils/storage';
import { saveToOPFS } from './opfs';

type FailureEvents = Extract<keyof DbEvents, 'close' | 'blocked'>;
export type FuelCachedAsset = AssetData &
Expand Down Expand Up @@ -47,6 +48,18 @@ export class FuelDB extends Dexie {
this.on('close', () => this.restart('close'));
}

async syncDbToOPFS() {
const accounts = await this.accounts.toArray();
const vaults = await this.vaults.toArray();
const networks = await this.networks.toArray();
const backupData = {
accounts,
vaults,
networks,
};
await saveToOPFS(backupData);
}

async syncDbToChromeStorage() {
const accounts = await this.accounts.toArray();
const vaults = await this.vaults.toArray();
Expand All @@ -55,23 +68,24 @@ export class FuelDB extends Dexie {
// @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) {
if (accounts.length && vaults.length && networks.length) {
console.log('saving data to chrome storage', {
accounts,
vaults,
networks,
});
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 || '',
Expand All @@ -93,6 +107,10 @@ export class FuelDB extends Dexie {
(() => this.syncDbToChromeStorage())();
} catch (_) {}

try {
(() => this.syncDbToOPFS())();
} catch (_) {}

try {
(async () => {
const accounts = await this.accounts.toArray();
Expand Down
Loading

0 comments on commit 4c7f835

Please sign in to comment.