Skip to content

Commit

Permalink
feat(dev-wallet): sort evrything bases on creation time (#2554)
Browse files Browse the repository at this point in the history
  • Loading branch information
javadkh2 authored Sep 30, 2024
1 parent 3221103 commit 601c6fd
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 82 deletions.
115 changes: 67 additions & 48 deletions packages/apps/dev-wallet/src/App/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, PropsWithChildren, useState } from 'react';
import { FC, PropsWithChildren, useEffect, useState } from 'react';
import {
Navigate,
Outlet,
Expand Down Expand Up @@ -56,6 +56,19 @@ const Redirect: FC<

return <> {children ? children : <Outlet />}</>;
};
const RouteContext: FC = () => {
const { isUnlocked, syncAllAccounts } = useWallet();
const location = useLocation();

// listen to general route changes and sync all accounts
useEffect(() => {
if (isUnlocked && syncAllAccounts) {
syncAllAccounts();
}
}, [location.pathname, isUnlocked, syncAllAccounts]);

return <Outlet />;
};

export const Routes: FC = () => {
const { isUnlocked } = useWallet();
Expand All @@ -68,57 +81,63 @@ export const Routes: FC = () => {
<CommunicationProvider children={<Outlet />} setOrigin={setOrigin} />
}
>
<Route element={<LayoutMini />}>
<Route element={<Redirect if={!isLocked} to={origin} />}>
<Route path="/select-profile" element={<SelectProfile />} />
<Route path="/create-profile/*" element={<CreateProfile />} />
<Route
path="/unlock-profile/:profileId"
element={<UnlockProfile />}
/>
<Route
path="/import-wallet"
element={<ImportWallet setOrigin={setOrigin} />}
/>
</Route>
</Route>
<Route
element={
<Redirect if={isLocked} to="/select-profile" setOrigin={setOrigin} />
}
>
<Route element={<RouteContext />}>
<Route element={<LayoutMini />}>
<Route
path="/backup-recovery-phrase"
element={<BackupRecoveryPhrase />}
/>
<Route
path="/backup-recovery-phrase/write-down"
element={<WriteDownRecoveryPhrase />}
/>
<Route
path="/account-discovery/:keySourceId"
element={<AccountDiscovery />}
/>
<Route element={<Redirect if={!isLocked} to={origin} />}>
<Route path="/select-profile" element={<SelectProfile />} />
<Route path="/create-profile/*" element={<CreateProfile />} />
<Route
path="/unlock-profile/:profileId"
element={<UnlockProfile />}
/>
<Route
path="/import-wallet"
element={<ImportWallet setOrigin={setOrigin} />}
/>
</Route>
</Route>
<Route element={<Layout />}>
<Route path="/" element={<HomePage />} />
<Route path="/sig-builder" element={<SignatureBuilder />} />
<Route path="/networks" element={<Networks />} />
<Route path="/networks/create" element={<CreateNetwork />} />
<Route path="/connect/:requestId" element={<Connect />} />
<Route path="/key-sources" element={<KeySources />} />
<Route path="/create-account" element={<CreateAccount />} />
<Route path="/transaction/:groupId" element={<TransactionPage />} />
<Route path="/transactions" element={<Transactions />} />
<Route path="/keyset/:keysetId" element={<Keyset />} />
<Route path="/fungible/:contract" element={<FungiblePage />} />
<Route path="/account/:accountId" element={<AccountPage />} />
<Route path="/transfer" element={<TransferV2 />} />
<Route
element={
<Redirect
if={isLocked}
to="/select-profile"
setOrigin={setOrigin}
/>
}
>
<Route element={<LayoutMini />}>
<Route
path="/backup-recovery-phrase"
element={<BackupRecoveryPhrase />}
/>
<Route
path="/backup-recovery-phrase/write-down"
element={<WriteDownRecoveryPhrase />}
/>
<Route
path="/account-discovery/:keySourceId"
element={<AccountDiscovery />}
/>
</Route>
<Route element={<Layout />}>
<Route path="/" element={<HomePage />} />
<Route path="/sig-builder" element={<SignatureBuilder />} />
<Route path="/networks" element={<Networks />} />
<Route path="/networks/create" element={<CreateNetwork />} />
<Route path="/connect/:requestId" element={<Connect />} />
<Route path="/key-sources" element={<KeySources />} />
<Route path="/create-account" element={<CreateAccount />} />
<Route path="/transaction/:groupId" element={<TransactionPage />} />
<Route path="/transactions" element={<Transactions />} />
<Route path="/keyset/:keysetId" element={<Keyset />} />
<Route path="/fungible/:contract" element={<FungiblePage />} />
<Route path="/account/:accountId" element={<AccountPage />} />
<Route path="/transfer" element={<TransferV2 />} />
</Route>
</Route>
<Route path="/ready" element={<Ready />} />
<Route path="*" element={<Heading>Not found!</Heading>} />
</Route>
<Route path="/ready" element={<Ready />} />
<Route path="*" element={<Heading>Not found!</Heading>} />
</Route>,
);

Expand Down
1 change: 1 addition & 0 deletions packages/apps/dev-wallet/src/modules/db/db.service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export interface IDBService {
storeName: string,
value: T,
key?: string | undefined,
options?: { noCreationTime: boolean },
) => Promise<void>;
update: <T>(
storeName: string,
Expand Down
32 changes: 29 additions & 3 deletions packages/apps/dev-wallet/src/modules/db/indexeddb.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const CREATION_TIME_KEY = 'creationTime';

export const createStore =
(db: IDBDatabase) =>
(
Expand Down Expand Up @@ -87,7 +89,23 @@ export const getAllItems =
reject(request.error);
};
request.onsuccess = () => {
resolve(request.result ?? []);
const result = request.result ?? [];
resolve(
result.sort((a: T, b: T) => {
if (
a &&
b &&
typeof a === 'object' &&
typeof b === 'object' &&
CREATION_TIME_KEY in a &&
CREATION_TIME_KEY in b &&
typeof a[CREATION_TIME_KEY] === 'number' &&
typeof b[CREATION_TIME_KEY] === 'number'
)
return b[CREATION_TIME_KEY] - a[CREATION_TIME_KEY];
return 0;
}),
);
};
});
};
Expand All @@ -110,11 +128,19 @@ export const getOneItem =

export const addItem =
(db: IDBDatabase) =>
<T>(storeName: string, value: T, key?: string) => {
<T>(
storeName: string,
value: T,
key?: string,
{ noCreationTime } = { noCreationTime: false },
) => {
return new Promise<void>((resolve, reject) => {
const transaction = db.transaction(storeName, 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.add(value, key);
const request = store.add(
noCreationTime ? value : { ...value, [CREATION_TIME_KEY]: Date.now() },
key,
);
request.onerror = () => {
reject(request.error);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
randomBytes,
} from '@kadena/hd-wallet';

import { IKeySource } from '@/modules/wallet/wallet.repository';
import {
IKeySource,
walletRepository,
} from '@/modules/wallet/wallet.repository';
import { IHDBIP44, keySourceRepository } from '../key-source.repository';
import { getNextAvailableIndex } from './utils';

Expand Down Expand Up @@ -44,7 +47,7 @@ export function createBIP44Service() {
): Promise<IHDBIP44> => {
const encryptedMnemonic = await kadenaEncrypt(password, mnemonic, 'buffer');
const secretId = crypto.randomUUID();
await keySourceRepository.addEncryptedValue(secretId, encryptedMnemonic);
await walletRepository.addEncryptedValue(secretId, encryptedMnemonic);
const keySource: IHDBIP44 = {
uuid: crypto.randomUUID(),
profileId,
Expand All @@ -59,7 +62,7 @@ export function createBIP44Service() {
};

const connect = async (password: string, keySource: IHDBIP44) => {
const encryptedMnemonic = await keySourceRepository.getEncryptedValue(
const encryptedMnemonic = await walletRepository.getEncryptedValue(
keySource.secretId,
);
const decryptedMnemonicBuffer = await kadenaDecrypt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
kadenaSign,
} from '@kadena/hd-wallet/chainweaver';

import { IKeySource } from '@/modules/wallet/wallet.repository';
import {
IKeySource,
walletRepository,
} from '@/modules/wallet/wallet.repository';
import { IHDChainweaver, keySourceRepository } from '../key-source.repository';
import { getNextAvailableIndex } from './utils';

Expand Down Expand Up @@ -69,8 +72,8 @@ export function createChainweaverService() {
mnemonic,
'buffer',
);
await keySourceRepository.addEncryptedValue(secretId, encryptedMnemonic);
await keySourceRepository.addEncryptedValue(rootKeyId, encryptedRootKey);
await walletRepository.addEncryptedValue(secretId, encryptedMnemonic);
await walletRepository.addEncryptedValue(rootKeyId, encryptedRootKey);
const keySource: IHDChainweaver = {
uuid: crypto.randomUUID(),
profileId,
Expand All @@ -85,7 +88,7 @@ export function createChainweaverService() {
},

connect: async (password: string, keySource: IHDChainweaver) => {
const encryptedRootKey = await keySourceRepository.getEncryptedValue(
const encryptedRootKey = await walletRepository.getEncryptedValue(
keySource.rootKeyId,
);
await kadenaDecrypt(password, encryptedRootKey);
Expand All @@ -107,7 +110,7 @@ export function createChainweaverService() {
throw new Error('Invalid key source');
}
const password = await decryptPassword(context);
const rootKey = await keySourceRepository.getEncryptedValue(
const rootKey = await walletRepository.getEncryptedValue(
keySource.rootKeyId,
);
const key = await kadenaGenKeypair(password, rootKey, startIndex);
Expand Down Expand Up @@ -138,15 +141,15 @@ export function createChainweaverService() {
}

const password = await decryptPassword(context);
const rootKey = await keySourceRepository.getEncryptedValue(
const rootKey = await walletRepository.getEncryptedValue(
keySource.rootKeyId,
);
const key = context.cache.has(`${keySourceId}-${keyIndex}`)
? (context.cache.get(`${keySourceId}-${keyIndex}`) as IKeyPair)
: await kadenaGenKeypair(password, rootKey, keyIndex);

const secretId = crypto.randomUUID();
await keySourceRepository.addEncryptedValue(
await walletRepository.addEncryptedValue(
secretId,
Buffer.from(key.secretKey, 'base64'),
);
Expand Down Expand Up @@ -174,9 +177,7 @@ export function createChainweaverService() {
.filter((key) => indexes.includes(key.index))
.map(async (key) => ({
...key,
secretKey: await keySourceRepository.getEncryptedValue(
key.secretId,
),
secretKey: await walletRepository.getEncryptedValue(key.secretId),
})),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ export interface HDWalletRepository {
getKeySource: (id: string) => Promise<KeySourceType>;
addKeySource: (profile: KeySourceType) => Promise<void>;
updateKeySource: (profile: KeySourceType) => Promise<void>;
getEncryptedValue: (key: string) => Promise<Uint8Array>;
addEncryptedValue: (key: string, value: string | Uint8Array) => Promise<void>;
}

const createKeySourceRepository = ({
Expand All @@ -61,15 +59,6 @@ const createKeySourceRepository = ({
updateKeySource: async (keySource: KeySourceType): Promise<void> => {
return update('keySource', keySource);
},
getEncryptedValue: async (key: string): Promise<Uint8Array> => {
return getOne('encryptedValue', key);
},
addEncryptedValue: async (
key: string,
value: string | Uint8Array,
): Promise<void> => {
return add('encryptedValue', value, key);
},
};
};

Expand Down
4 changes: 3 additions & 1 deletion packages/apps/dev-wallet/src/modules/wallet/wallet.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const isUnlocked = (
};

export const useWallet = () => {
const [context, setProfile, setActiveNetwork] =
const [context, setProfile, setActiveNetwork, syncAllAccounts] =
useContext(WalletContext) ?? [];
const prompt = usePrompt();
if (!context || !setProfile) {
Expand Down Expand Up @@ -50,6 +50,7 @@ export const useWallet = () => {

const unlockProfile = useCallback(
async (profileId: string, password: string) => {
console.log('unlockProfile', profileId, password);
const profile = await WalletService.unlockProfile(profileId, password);
if (profile) {
return setProfile(profile);
Expand Down Expand Up @@ -251,5 +252,6 @@ export const useWallet = () => {
keysets: context.keysets || [],
keySources: context.keySources || [],
fungibles: context.fungibles || [],
syncAllAccounts,
};
};
15 changes: 11 additions & 4 deletions packages/apps/dev-wallet/src/modules/wallet/wallet.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ export type ExtWalletContextType = {
export const WalletContext = createContext<
| [
ExtWalletContextType,
(profile: IProfile | undefined) => Promise<null | {
setProfile: (profile: IProfile | undefined) => Promise<null | {
profile: IProfile;
accounts: IAccount[];
keySources: IKeySource[];
}>,
(activeNetwork: INetwork | undefined) => void,
setActiveNetwork: (activeNetwork: INetwork | undefined) => void,
syncAllAccounts: () => void,
]
| null
>(null);
Expand Down Expand Up @@ -233,11 +234,17 @@ export const WalletProvider: FC<PropsWithChildren> = ({ children }) => {
console.log('retrieving accounts');
retrieveAccounts(contextValue.profile.uuid);
}
}, [contextValue.activeNetwork?.networkId]);
}, [retrieveAccounts, contextValue.profile?.uuid]);

const syncAllAccountsCb = useCallback(() => {
if (contextValue.profile?.uuid) {
syncAllAccounts(contextValue.profile?.uuid);
}
}, [contextValue.profile?.uuid]);

return (
<WalletContext.Provider
value={[contextValue, setProfile, setActiveNetwork]}
value={[contextValue, setProfile, setActiveNetwork, syncAllAccountsCb]}
>
{contextValue.loaded ? children : 'Loading wallet...'}
</WalletContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const createWalletRepository = ({
key: string,
value: string | Uint8Array,
): Promise<void> => {
return add('encryptedValue', value, key);
return add('encryptedValue', value, key, { noCreationTime: true });
},
getProfileKeySources: async (profileId: string): Promise<IKeySource[]> => {
return getAll('keySource', profileId, 'profileId');
Expand Down
Loading

0 comments on commit 601c6fd

Please sign in to comment.