Add a button to delete token accounts

This commit is contained in:
Gary Wang 2020-10-05 03:13:24 -07:00
parent ee8018c6fd
commit 02b4748fb5
6 changed files with 128 additions and 7 deletions

View File

@ -21,6 +21,7 @@ import { abbreviateAddress } from '../utils/utils';
import Button from '@material-ui/core/Button';
import SendIcon from '@material-ui/icons/Send';
import ReceiveIcon from '@material-ui/icons/WorkOutline';
import DeleteIcon from '@material-ui/icons/Delete';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import AddIcon from '@material-ui/icons/Add';
@ -36,6 +37,7 @@ import {
useSolanaExplorerUrlSuffix,
} from '../utils/connection';
import { showTokenInfoDialog } from '../utils/config';
import CloseTokenAccountDialog from './CloseTokenAccountButton';
const balanceFormat = new Intl.NumberFormat(undefined, {
minimumFractionDigits: 4,
@ -150,12 +152,16 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
const [sendDialogOpen, setSendDialogOpen] = useState(false);
const [depositDialogOpen, setDepositDialogOpen] = useState(false);
const [tokenInfoDialogOpen, setTokenInfoDialogOpen] = useState(false);
const [
closeTokenAccountDialogOpen,
setCloseTokenAccountDialogOpen,
] = useState(false);
if (!balanceInfo) {
return <LoadingIndicator delay={0} />;
}
let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
let { mint, tokenName, tokenSymbol, owner, amount } = balanceInfo;
return (
<>
@ -187,6 +193,17 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
>
Send
</Button>
{mint && amount === 0 ? (
<Button
variant="outlined"
color="secondary"
size="small"
startIcon={<DeleteIcon />}
onClick={() => setCloseTokenAccountDialogOpen(true)}
>
Delete
</Button>
) : null}
</div>
<Typography variant="body2" className={classes.address}>
Deposit Address: {publicKey.toBase58()}
@ -233,6 +250,12 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
balanceInfo={balanceInfo}
publicKey={publicKey}
/>
<CloseTokenAccountDialog
open={closeTokenAccountDialogOpen}
onClose={() => setCloseTokenAccountDialogOpen(false)}
balanceInfo={balanceInfo}
publicKey={publicKey}
/>
</>
);
}

View File

@ -0,0 +1,52 @@
import DialogForm from './DialogForm';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
import { DialogContentText } from '@material-ui/core';
import { abbreviateAddress } from '../utils/utils';
import DialogActions from '@material-ui/core/DialogActions';
import Button from '@material-ui/core/Button';
import React from 'react';
import { useSendTransaction } from '../utils/notifications';
import { refreshWalletPublicKeys, useWallet } from '../utils/wallet';
export default function CloseTokenAccountDialog({
open,
onClose,
publicKey,
balanceInfo,
}) {
const wallet = useWallet();
const [sendTransaction, sending] = useSendTransaction();
const { mint, tokenName } = balanceInfo;
function onSubmit() {
sendTransaction(wallet.closeTokenAccount(publicKey), {
onSuccess: () => {
refreshWalletPublicKeys(wallet);
onClose();
},
});
}
return (
<DialogForm open={open} onClose={onClose} onSubmit={onSubmit}>
<DialogTitle>
Delete {tokenName ?? mint.toBase58()} Address{' '}
{abbreviateAddress(publicKey)}
</DialogTitle>
<DialogContent>
<DialogContentText>
Are you sure you want to delete your {tokenName ?? mint.toBase58()}{' '}
address {publicKey.toBase58()}? This will permanently disable token
transfers to this address and remove it from your wallet.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Cancel</Button>
<Button type="submit" color="secondary" disabled={sending}>
Delete
</Button>
</DialogActions>
</DialogForm>
);
}

View File

@ -59,14 +59,16 @@ export function useAccountInfo(publicKey) {
if (!publicKey) {
return;
}
let previousData = null;
let previousInfo = null;
const id = connection.onAccountChange(publicKey, (info) => {
if (info.data) {
if (!previousData || !previousData.equals(info.data)) {
previousData = info.data;
if (
!previousInfo ||
!previousInfo.data.equals(info.data) ||
previousInfo.lamports !== info.lamports
) {
previousInfo = info;
setCache(cacheKey, info);
}
}
});
return () => connection.removeAccountChangeListener(id);
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@ -1,5 +1,6 @@
import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
import {
closeAccount,
initializeAccount,
initializeMint,
memoInstruction,
@ -173,3 +174,21 @@ export async function transferTokens({
preflightCommitment: 'single',
});
}
export async function closeTokenAccount({
connection,
owner,
sourcePublicKey,
}) {
let transaction = new Transaction().add(
closeAccount({
source: sourcePublicKey,
destination: owner.publicKey,
owner: owner.publicKey,
}),
);
let signers = [owner];
return await connection.sendTransaction(transaction, signers, {
preflightCommitment: 'single',
});
}

View File

@ -44,6 +44,7 @@ LAYOUT.addVariant(
BufferLayout.struct([BufferLayout.nu64('amount')]),
'burn',
);
LAYOUT.addVariant(9, BufferLayout.struct([]), 'closeAccount');
const instructionMaxSpan = Math.max(
...Object.values(LAYOUT.registry).map((r) => r.span),
@ -127,6 +128,21 @@ export function mintTo({ mint, destination, amount, mintAuthority }) {
});
}
export function closeAccount({ source, destination, owner }) {
const keys = [
{ pubkey: source, isSigner: false, isWritable: true },
{ pubkey: destination, isSigner: false, isWritable: true },
{ pubkey: owner, isSigner: true, isWritable: false },
];
return new TransactionInstruction({
keys,
data: encodeTokenInstructionData({
closeAccount: {},
}),
programId: TOKEN_PROGRAM_ID,
});
}
export function memoInstruction(memo) {
return new TransactionInstruction({
keys: [],

View File

@ -8,6 +8,7 @@ import {
useConnection,
} from './connection';
import {
closeTokenAccount,
createAndInitializeTokenAccount,
getOwnedTokenAccounts,
transferTokens,
@ -97,6 +98,14 @@ export class Wallet {
preflightCommitment: 'single',
});
};
closeTokenAccount = async (publicKey) => {
return await closeTokenAccount({
connection: this.connection,
owner: this.account,
sourcePublicKey: publicKey,
});
};
}
const WalletContext = React.createContext(null);