Skip to content

Commit

Permalink
feat: add opfs backup layer
Browse files Browse the repository at this point in the history
  • Loading branch information
LuizAsFight committed Dec 27, 2024
1 parent 7fcfee7 commit 532d16c
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 51 deletions.
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
35 changes: 35 additions & 0 deletions packages/app/src/systems/Core/utils/opfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
async function initOPFS() {
const root = await navigator.storage.getDirectory();

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 1: Connections › should do the entire connection management flow

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17 TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 2: Connections › should do the entire connection management flow

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17 TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 4: Connections › should do the entire connection management flow

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17 TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check failure on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

Connections › should do the entire connection management flow

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17 TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 1: importAccountMachine › import › should be able to import from private key

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 1: importAccountMachine › import › should throw a error if private key is already imported

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 1: ConnectionEdit › should disconnect account

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 2: ConnectionEdit › should disconnect account

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 3: ConnectionEdit › should disconnect account

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check failure on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

ConnectionEdit › should disconnect account

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17

Check warning on line 2 in packages/app/src/systems/Core/utils/opfs.ts

View workflow job for this annotation

GitHub Actions / Test

RETRY 1: addAccountMachine › add › should be able to add an account

Expected test not to call console.error(). If the error is expected, test for it explicitly by mocking it out using jest.spyOn(console, 'error').mockImplementation() and test that the warning occurs. TypeError: Cannot read properties of undefined (reading 'getDirectory') at initOPFS (src/systems/Core/utils/opfs.ts:2:40) at readFromOPFS (src/systems/Core/utils/opfs.ts:25:22) at Function.fetchRecoveryState (src/systems/Account/services/account.ts:227:19) at fetch (src/systems/Account/machines/accountsMachine.tsx:293:58) at logError (src/systems/Core/machines/fetchMachine.ts:117:21) at Interpreter._exec (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:171:18) at handleAction (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:596:11) at processBlock (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:627:24) at Array.map (<anonymous>) at Object.resolveActions (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/actions.js:652:52) at StateNode.resolveTransition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:917:36) at StateNode.transition (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/StateNode.js:840:17) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:956:28 at Object.provide (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/serviceScope.js:12:16) at Interpreter._nextState (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:955:34) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:108:31 at Scheduler.process (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:69:7) at Scheduler.schedule (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/scheduler.js:48:10) at Interpreter.send (../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:104:23) at ../../node_modules/.pnpm/[email protected]/node_modules/xstate/lib/interpreter.js:1151:17
return root;
}

// biome-ignore lint/suspicious/noExplicitAny: <explanation>
export async function saveToOPFS(data: any) {
if (
!data.accounts?.length ||
!data.vaults?.length ||
!data.networks?.length
) {
return;
}

console.log('saving data to opfs', data);
const root = await initOPFS();
const fileHandle = await root.getFileHandle('backup.json', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(JSON.stringify(data));
await writable.close();
}

export async function readFromOPFS() {
const root = await initOPFS();
try {
const fileHandle = await root.getFileHandle('backup.json');
const file = await fileHandle.getFile();
const text = await file.text();
return JSON.parse(text);
} catch (_) {
// Create empty backup file if it doesn't exist
return {};
}
}

0 comments on commit 532d16c

Please sign in to comment.