Skip to content

Commit

Permalink
fix: (Delete) handling multiple wallets and delete order error
Browse files Browse the repository at this point in the history
  • Loading branch information
frichards committed Dec 17, 2024
1 parent 1e092ff commit 6192af5
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 47 deletions.
136 changes: 111 additions & 25 deletions src/background/services/accounts/handlers/avalanche_deleteAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,21 @@ import { canSkipApproval } from '@src/utils/canSkipApproval';

import { AccountsService } from '../AccountsService';
import { Action } from '../../actions/models';
import { Account, ImportedAccount, PrimaryAccount } from '../models';
import { ImportedAccount, PrimaryAccount } from '../models';
import { isPrimaryAccount } from '../utils/typeGuards';
import { SecretsService } from '../../secrets/SecretsService';

type PrimaryWalletAccounts = {
[walletId: string]: PrimaryAccount[];
};

export type DeleteAccountsDisplayData = {
primary: PrimaryWalletAccounts;
imported: ImportedAccount[];
wallet: {
[walletId: string]: string;
};
};

type Params = [accountIds: string[]];

Expand All @@ -23,7 +37,10 @@ export class AvalancheDeleteAccountsHandler extends DAppRequestHandler<
> {
methods = [DAppProviderRequest.ACCOUNTS_DELETE];

constructor(private accountsService: AccountsService) {
constructor(
private accountsService: AccountsService,
private secretsService: SecretsService
) {
super();
}

Expand All @@ -33,6 +50,10 @@ export class AvalancheDeleteAccountsHandler extends DAppRequestHandler<
const { request, scope } = rpcCall;
const [accountIds] = request.params;

//TODO DELETE THIS
accountIds.unshift('1d6ad089-52b3-40b9-a3e5-ac5a970ba063');
accountIds.push('75b6b83d-2836-4997-a21f-3ee42f8e6a38');

if (!request.site?.domain || !request.site?.tabId) {
return {
...request,
Expand All @@ -42,17 +63,33 @@ export class AvalancheDeleteAccountsHandler extends DAppRequestHandler<
};
}
console.log({ accountIds });
const accounts = accountIds.reduce((accumulator, accountId) => {

const currentAccounts = this.accountsService.getAccounts();
console.log({ currentAccounts });

const primaryWalletAccounts: PrimaryWalletAccounts = {};
const importedAccounts: ImportedAccount[] = [];

// Getting accounts by ids from params and organizing
for (const accountId of accountIds) {
const account = this.accountsService.getAccountByID(accountId);
if (account) {
accumulator[accountId] = account;
if (isPrimaryAccount(account)) {
if (primaryWalletAccounts[account.walletId]) {
primaryWalletAccounts[account.walletId]?.push(account);
} else {
primaryWalletAccounts[account.walletId] = [account];
}
} else {
importedAccounts.push(account);
}
}
}

return accumulator;
}, {} as Record<string, ImportedAccount | PrimaryAccount>);
console.log({ accounts });

if (!Object.keys(accounts).length) {
if (
!Object.keys(primaryWalletAccounts).length &&
!importedAccounts.length
) {
return {
...request,
error: ethErrors.rpc.invalidParams({
Expand All @@ -61,30 +98,79 @@ export class AvalancheDeleteAccountsHandler extends DAppRequestHandler<
};
}

if (await canSkipApproval(request.site.domain, request.site.tabId)) {
try {
await this.accountsService.deleteAccounts(Object.keys(accounts));

return {
...request,
result: null,
};
} catch (err) {
console.log(err);
return {
...request,
error: ethErrors.rpc.internal('Account removing failed'),
};
//Checking if the accounts to be deleted has the latest index
for (const [walletId, accountsInWallet] of Object.entries(
primaryWalletAccounts
)) {
console.log(`${walletId}: ${accountsInWallet}`);
accountsInWallet.sort((a, b) => b.index - a.index);
console.log({ sorted: accountsInWallet });

const walletAccounts =
this.accountsService.getPrimaryAccountsByWalletId(walletId);
console.log({ walletAccounts });

for (let i = 0; i < accountsInWallet.length; i++) {
const accountToDelete = accountsInWallet[i];
const accountInWallet = walletAccounts[walletAccounts.length - 1 - i];
if (
!accountToDelete ||
!accountInWallet ||
accountToDelete.id !== accountInWallet.id
) {
return {
...request,
error: ethErrors.rpc.invalidParams({
message: 'Only the last account of the wallet can be removed',
}),
};
}
}
}

//TODO Uncomment this block
// if (await canSkipApproval(request.site.domain, request.site.tabId)) {
// try {
// await this.accountsService.deleteAccounts(Object.keys(accounts));

// return {
// ...request,
// result: null,
// };
// } catch (err) {
// console.log(err);
// return {
// ...request,
// error: ethErrors.rpc.internal('Account removing failed'),
// };
// }
// }

const walletNames: Record<string, string> = {};

const primaryWallets = await this.secretsService.getPrimaryWalletsDetails();

for (const walletId of Object.keys(primaryWalletAccounts)) {
const primaryWallet = primaryWallets.find(
(wallet) => wallet.id === walletId
);

if (primaryWallet?.name) {
walletNames[walletId] = primaryWallet.name;
}
}

const actionData: Action<{
accounts: Record<string, ImportedAccount | PrimaryAccount>;
accounts: DeleteAccountsDisplayData;
}> = {
...request,
scope,
displayData: {
accounts,
accounts: {
primary: primaryWalletAccounts,
imported: importedAccounts,
wallet: walletNames,
},
},
};

Expand Down
95 changes: 73 additions & 22 deletions src/pages/Wallet/DeleteAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import {
Stack,
Typography,
} from '@avalabs/core-k2-components';

import type {
ImportedAccount,
PrimaryAccount,
} from '@src/background/services/accounts/models';
import { useApproveAction } from '@src/hooks/useApproveAction';
import { ActionStatus } from '@src/background/services/actions/models';
import { useGetRequestId } from '@src/hooks/useGetRequestId';
Expand All @@ -21,13 +16,14 @@ import {
import { WebsiteDetails } from '../SignTransaction/components/ApprovalTxDetails';
import type { DomainMetadata } from '@src/background/models';
import { truncateAddress } from '@src/utils/truncateAddress';
import type { DeleteAccountsDisplayData } from '@src/background/services/accounts/handlers/avalanche_deleteAccounts';

export function DeleteAccount() {
const { t } = useTranslation();
const requestId = useGetRequestId();

const { action, updateAction, cancelHandler } = useApproveAction<{
accounts: Record<string, ImportedAccount | PrimaryAccount>;
accounts: DeleteAccountsDisplayData;
}>(requestId);

if (!action) {
Expand All @@ -45,6 +41,13 @@ export function DeleteAccount() {
);
}

const primaryAccountsCount = Object.values(
action.displayData.accounts.primary
).flat().length;

const accountsCount =
action.displayData.accounts.imported.length + primaryAccountsCount;

const site: DomainMetadata = action.site ?? {
domain: '#',
name: t('Unknown website'),
Expand All @@ -54,9 +57,7 @@ export function DeleteAccount() {
<Stack sx={{ width: 1, px: 2 }}>
<Stack sx={{ flexGrow: 1, width: 1, gap: 3 }}>
<Typography variant="h4" sx={{ mt: 1.5, mb: 3.5 }}>
{Object.keys(action.displayData.accounts).length === 1
? t('Delete account?')
: t('Delete accounts?')}
{accountsCount === 1 ? t('Delete Account?') : t('Delete Accounts?')}
</Typography>
<ApprovalSection>
<ApprovalSectionHeader label={t('Action Details')} />
Expand All @@ -67,26 +68,75 @@ export function DeleteAccount() {
<ApprovalSection>
<ApprovalSectionHeader
label={
Object.keys(action.displayData.accounts).length === 1
? t('Account to delete')
: t('Accounts to delete')
accountsCount === 1
? t('Account to be Deleted')
: t('Accounts to be Deleted')
}
/>
<ApprovalSectionBody sx={{ py: 1, px: 2, gap: 1 }}>
{Object.values(action.displayData.accounts).map((account, i) => (
<Stack
direction={'row'}
sx={{ justifyContent: 'space-between' }}
key={account.id}
>
<Typography>{account.name}</Typography>
<Typography>{truncateAddress(account.addressC)}</Typography>
{Object.keys(action.displayData.accounts.primary) &&
Object.entries(action.displayData.accounts.primary).map(
([walletId, primaryAccounts], i) => (
<Stack key={walletId} sx={{ mt: i === 0 ? 0 : 2 }}>
<Stack>
<Typography sx={{ fontWeight: 600, mb: 1 }}>
{action.displayData.accounts.wallet[walletId]}
</Typography>
</Stack>
{primaryAccounts.map((primaryAccount) => (
<Stack
direction={'row'}
sx={{ justifyContent: 'space-between' }}
key={primaryAccount.id}
>
<Typography>{primaryAccount.name}</Typography>
<Typography>
{truncateAddress(primaryAccount.addressC)}
</Typography>
</Stack>
))}
</Stack>
)
)}

{action.displayData.accounts.imported.length && (
<Stack sx={{ mt: 2 }}>
<Typography sx={{ fontWeight: 600, mb: 1 }}>
{action.displayData.accounts.imported.length === 1
? t('Imported Account')
: t('Imported Accounts')}
</Typography>
{action.displayData.accounts.imported.map((importedAccount) => (
<Stack key={importedAccount.id}>
<Stack
direction={'row'}
sx={{ justifyContent: 'space-between' }}
key={importedAccount.id}
>
<Typography>{importedAccount.name}</Typography>
<Typography>
{truncateAddress(importedAccount.addressC)}
</Typography>
</Stack>
</Stack>
))}
</Stack>
))}
)}
</ApprovalSectionBody>
</ApprovalSection>
</Stack>
<Stack sx={{ width: 1, justifyContent: 'space-between' }}>
<Stack>
<Typography
variant="caption"
color="text.secondary"
sx={{ textAlign: 'center', mb: 2 }}
>
{t(
'Removing the account will delete all local account information stored on this computer. Your assets on chain will remain on chain.'
)}
</Typography>
</Stack>
<Stack direction="row" sx={{ gap: 1 }}>
<Button
color="secondary"
Expand All @@ -104,6 +154,7 @@ export function DeleteAccount() {
data-testid="switch-account-approve-btn"
size="large"
color="primary"
sx={{ color: 'error.main' }}
onClick={() => {
updateAction({
status: ActionStatus.SUBMITTING,
Expand All @@ -112,7 +163,7 @@ export function DeleteAccount() {
}}
fullWidth
>
{t('Approve')}
{t('Delete')}
</Button>
</Stack>
</Stack>
Expand Down

0 comments on commit 6192af5

Please sign in to comment.