diff --git a/src/components/BalancesList.js b/src/components/BalancesList.js
index f795bfb..ea410d9 100644
--- a/src/components/BalancesList.js
+++ b/src/components/BalancesList.js
@@ -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 ;
}
- let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
+ let { mint, tokenName, tokenSymbol, owner, amount } = balanceInfo;
return (
<>
@@ -187,6 +193,17 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
>
Send
+ {mint && amount === 0 ? (
+ }
+ onClick={() => setCloseTokenAccountDialogOpen(true)}
+ >
+ Delete
+
+ ) : null}
Deposit Address: {publicKey.toBase58()}
@@ -233,6 +250,12 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
balanceInfo={balanceInfo}
publicKey={publicKey}
/>
+ setCloseTokenAccountDialogOpen(false)}
+ balanceInfo={balanceInfo}
+ publicKey={publicKey}
+ />
>
);
}
diff --git a/src/components/CloseTokenAccountButton.js b/src/components/CloseTokenAccountButton.js
new file mode 100644
index 0000000..33852c3
--- /dev/null
+++ b/src/components/CloseTokenAccountButton.js
@@ -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 (
+
+
+ Delete {tokenName ?? mint.toBase58()} Address{' '}
+ {abbreviateAddress(publicKey)}
+
+
+
+ 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.
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/utils/connection.js b/src/utils/connection.js
index 4c32255..06b3ef8 100644
--- a/src/utils/connection.js
+++ b/src/utils/connection.js
@@ -59,13 +59,15 @@ 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;
- setCache(cacheKey, info);
- }
+ if (
+ !previousInfo ||
+ !previousInfo.data.equals(info.data) ||
+ previousInfo.lamports !== info.lamports
+ ) {
+ previousInfo = info;
+ setCache(cacheKey, info);
}
});
return () => connection.removeAccountChangeListener(id);
diff --git a/src/utils/tokens/index.js b/src/utils/tokens/index.js
index 898d1da..0571439 100644
--- a/src/utils/tokens/index.js
+++ b/src/utils/tokens/index.js
@@ -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',
+ });
+}
diff --git a/src/utils/tokens/instructions.js b/src/utils/tokens/instructions.js
index e2c8aae..5916458 100644
--- a/src/utils/tokens/instructions.js
+++ b/src/utils/tokens/instructions.js
@@ -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: [],
diff --git a/src/utils/wallet.js b/src/utils/wallet.js
index 4f50e1c..0d1e2e1 100644
--- a/src/utils/wallet.js
+++ b/src/utils/wallet.js
@@ -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);