Add a button to delete token accounts
This commit is contained in:
parent
ee8018c6fd
commit
02b4748fb5
|
@ -21,6 +21,7 @@ import { abbreviateAddress } from '../utils/utils';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import SendIcon from '@material-ui/icons/Send';
|
import SendIcon from '@material-ui/icons/Send';
|
||||||
import ReceiveIcon from '@material-ui/icons/WorkOutline';
|
import ReceiveIcon from '@material-ui/icons/WorkOutline';
|
||||||
|
import DeleteIcon from '@material-ui/icons/Delete';
|
||||||
import AppBar from '@material-ui/core/AppBar';
|
import AppBar from '@material-ui/core/AppBar';
|
||||||
import Toolbar from '@material-ui/core/Toolbar';
|
import Toolbar from '@material-ui/core/Toolbar';
|
||||||
import AddIcon from '@material-ui/icons/Add';
|
import AddIcon from '@material-ui/icons/Add';
|
||||||
|
@ -36,6 +37,7 @@ import {
|
||||||
useSolanaExplorerUrlSuffix,
|
useSolanaExplorerUrlSuffix,
|
||||||
} from '../utils/connection';
|
} from '../utils/connection';
|
||||||
import { showTokenInfoDialog } from '../utils/config';
|
import { showTokenInfoDialog } from '../utils/config';
|
||||||
|
import CloseTokenAccountDialog from './CloseTokenAccountButton';
|
||||||
|
|
||||||
const balanceFormat = new Intl.NumberFormat(undefined, {
|
const balanceFormat = new Intl.NumberFormat(undefined, {
|
||||||
minimumFractionDigits: 4,
|
minimumFractionDigits: 4,
|
||||||
|
@ -150,12 +152,16 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
|
||||||
const [sendDialogOpen, setSendDialogOpen] = useState(false);
|
const [sendDialogOpen, setSendDialogOpen] = useState(false);
|
||||||
const [depositDialogOpen, setDepositDialogOpen] = useState(false);
|
const [depositDialogOpen, setDepositDialogOpen] = useState(false);
|
||||||
const [tokenInfoDialogOpen, setTokenInfoDialogOpen] = useState(false);
|
const [tokenInfoDialogOpen, setTokenInfoDialogOpen] = useState(false);
|
||||||
|
const [
|
||||||
|
closeTokenAccountDialogOpen,
|
||||||
|
setCloseTokenAccountDialogOpen,
|
||||||
|
] = useState(false);
|
||||||
|
|
||||||
if (!balanceInfo) {
|
if (!balanceInfo) {
|
||||||
return <LoadingIndicator delay={0} />;
|
return <LoadingIndicator delay={0} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { mint, tokenName, tokenSymbol, owner } = balanceInfo;
|
let { mint, tokenName, tokenSymbol, owner, amount } = balanceInfo;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -187,6 +193,17 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
</Button>
|
</Button>
|
||||||
|
{mint && amount === 0 ? (
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
size="small"
|
||||||
|
startIcon={<DeleteIcon />}
|
||||||
|
onClick={() => setCloseTokenAccountDialogOpen(true)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<Typography variant="body2" className={classes.address}>
|
<Typography variant="body2" className={classes.address}>
|
||||||
Deposit Address: {publicKey.toBase58()}
|
Deposit Address: {publicKey.toBase58()}
|
||||||
|
@ -233,6 +250,12 @@ function BalanceListItemDetails({ publicKey, balanceInfo }) {
|
||||||
balanceInfo={balanceInfo}
|
balanceInfo={balanceInfo}
|
||||||
publicKey={publicKey}
|
publicKey={publicKey}
|
||||||
/>
|
/>
|
||||||
|
<CloseTokenAccountDialog
|
||||||
|
open={closeTokenAccountDialogOpen}
|
||||||
|
onClose={() => setCloseTokenAccountDialogOpen(false)}
|
||||||
|
balanceInfo={balanceInfo}
|
||||||
|
publicKey={publicKey}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -59,14 +59,16 @@ export function useAccountInfo(publicKey) {
|
||||||
if (!publicKey) {
|
if (!publicKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let previousData = null;
|
let previousInfo = null;
|
||||||
const id = connection.onAccountChange(publicKey, (info) => {
|
const id = connection.onAccountChange(publicKey, (info) => {
|
||||||
if (info.data) {
|
if (
|
||||||
if (!previousData || !previousData.equals(info.data)) {
|
!previousInfo ||
|
||||||
previousData = info.data;
|
!previousInfo.data.equals(info.data) ||
|
||||||
|
previousInfo.lamports !== info.lamports
|
||||||
|
) {
|
||||||
|
previousInfo = info;
|
||||||
setCache(cacheKey, info);
|
setCache(cacheKey, info);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return () => connection.removeAccountChangeListener(id);
|
return () => connection.removeAccountChangeListener(id);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
|
import { PublicKey, SystemProgram, Transaction } from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
|
closeAccount,
|
||||||
initializeAccount,
|
initializeAccount,
|
||||||
initializeMint,
|
initializeMint,
|
||||||
memoInstruction,
|
memoInstruction,
|
||||||
|
@ -173,3 +174,21 @@ export async function transferTokens({
|
||||||
preflightCommitment: 'single',
|
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',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ LAYOUT.addVariant(
|
||||||
BufferLayout.struct([BufferLayout.nu64('amount')]),
|
BufferLayout.struct([BufferLayout.nu64('amount')]),
|
||||||
'burn',
|
'burn',
|
||||||
);
|
);
|
||||||
|
LAYOUT.addVariant(9, BufferLayout.struct([]), 'closeAccount');
|
||||||
|
|
||||||
const instructionMaxSpan = Math.max(
|
const instructionMaxSpan = Math.max(
|
||||||
...Object.values(LAYOUT.registry).map((r) => r.span),
|
...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) {
|
export function memoInstruction(memo) {
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys: [],
|
keys: [],
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
useConnection,
|
useConnection,
|
||||||
} from './connection';
|
} from './connection';
|
||||||
import {
|
import {
|
||||||
|
closeTokenAccount,
|
||||||
createAndInitializeTokenAccount,
|
createAndInitializeTokenAccount,
|
||||||
getOwnedTokenAccounts,
|
getOwnedTokenAccounts,
|
||||||
transferTokens,
|
transferTokens,
|
||||||
|
@ -97,6 +98,14 @@ export class Wallet {
|
||||||
preflightCommitment: 'single',
|
preflightCommitment: 'single',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
closeTokenAccount = async (publicKey) => {
|
||||||
|
return await closeTokenAccount({
|
||||||
|
connection: this.connection,
|
||||||
|
owner: this.account,
|
||||||
|
sourcePublicKey: publicKey,
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const WalletContext = React.createContext(null);
|
const WalletContext = React.createContext(null);
|
||||||
|
|
Loading…
Reference in New Issue