transfer-token
Version:
Plugin for transferring tokens to different wallets in API City
202 lines (173 loc) • 6.66 kB
text/typescript
import { Connection, PublicKey, Transaction } from '@solana/web3.js';
import { TOKEN_PROGRAM_ID, getAccount, createTransferInstruction, getAssociatedTokenAddress } from '@solana/spl-token';
import { BasePlugin, PluginContext } from 'api-city-sdk';
export class TransferTokenPlugin extends BasePlugin {
protected context!: PluginContext;
private connection: Connection;
private readonly BASE_COMMAND = 'transfer';
constructor() {
super({
name: "Token Transfer Manager",
description: "Transfer tokens to different wallets on Solana",
version: "1.0.0",
author: "API City",
permissions: [],
entryPoints: {
main: "transfer"
}
});
// Initialize connection
this.connection = new Connection(
process.env.NEXT_PUBLIC_SOLANA_RPC || 'https://api.mainnet-beta.solana.com',
{ commitment: 'confirmed' }
);
}
async initialize(context: PluginContext): Promise<void> {
this.context = context;
}
render(): React.ReactNode {
return null;
}
private async getTokenAccounts(publicKey: PublicKey): Promise<{ pubkey: PublicKey; account: any }[]> {
const accounts = await this.connection.getParsedTokenAccountsByOwner(publicKey, {
programId: TOKEN_PROGRAM_ID,
});
return accounts.value.filter(({ account }) => {
const info = account.data.parsed.info;
return Number(info.tokenAmount.amount) > 0;
});
}
private async transferTokens(
fromPublicKey: PublicKey,
toPublicKey: PublicKey,
sourceTokenAccount: PublicKey,
amount: number
): Promise<void> {
try {
const destinationTokenAccount = await getAssociatedTokenAddress(
new PublicKey(sourceTokenAccount.toString()),
toPublicKey
);
const latestBlockhash = await this.connection.getLatestBlockhash();
const transaction = new Transaction(latestBlockhash);
transaction.feePayer = fromPublicKey;
transaction.add(
createTransferInstruction(
sourceTokenAccount,
destinationTokenAccount,
fromPublicKey,
amount,
[],
TOKEN_PROGRAM_ID
)
);
if (!this.context.wallet.signTransaction) {
throw new Error("Wallet does not support transaction signing");
}
this.emit('message', { content: "Sending transaction for approval..." });
const signedTransaction = await this.context.wallet.signTransaction(transaction);
const signature = await this.connection.sendRawTransaction(
signedTransaction.serialize(),
{ preflightCommitment: 'confirmed', maxRetries: 10 }
);
await this.connection.confirmTransaction({
signature,
...latestBlockhash
});
this.emit('message', {
content: `Successfully transferred tokens.\nTransaction: https://solscan.io/tx/${signature}`
});
} catch (error: any) {
throw new Error(`Transfer failed: ${error.message}`);
}
}
async handleCommand(command: string, args: string[]): Promise<void> {
if (!command.startsWith(this.BASE_COMMAND)) {
this.emit('message', { content: "Unknown command" });
return;
}
try {
const publicKey = this.context.wallet.publicKey;
if (!publicKey) {
this.emit('message', { content: "Wallet not connected" });
return;
}
const subCommand = args[0] || 'help';
switch (subCommand) {
case 'help':
this.emit('message', { content: `Available commands:
- /transfer help: Show this help message
- /transfer list: Show list of token accounts with balances
- /transfer [token_account] [recipient_address] [amount]: Transfer tokens to another wallet` });
break;
case 'list':
const tokenAccounts = await this.getTokenAccounts(publicKey);
if (tokenAccounts.length === 0) {
this.emit('message', { content: "No token accounts with balance found" });
} else {
const accountList = tokenAccounts.map(({ pubkey, account }, index) => {
const info = account.data.parsed.info;
return `${index + 1}. ${pubkey.toString()} - Balance: ${info.tokenAmount.uiAmount} ${info.mint}`;
}).join('\n');
this.emit('message', {
content: `Found ${tokenAccounts.length} token accounts with balance:\n${accountList}`
});
}
break;
default:
// Handle transfer
if (args.length !== 3) {
this.emit('message', { content: "Please provide: token_account recipient_address amount" });
return;
}
const [tokenAccount, recipientAddress, amountStr] = args;
try {
const sourceAccount = new PublicKey(tokenAccount);
const recipientPublicKey = new PublicKey(recipientAddress);
const amount = parseFloat(amountStr);
if (isNaN(amount) || amount <= 0) {
throw new Error("Invalid amount");
}
await this.transferTokens(
publicKey,
recipientPublicKey,
sourceAccount,
amount
);
} catch (err: any) {
this.emit('message', { content: `Invalid parameters: ${err.message}` });
return;
}
break;
}
} catch (err: any) {
console.error("Error in command execution:", err);
this.emit('message', { content: `Error: ${err.message}` });
}
}
getCommands(): { command: string; description: string; usage: string; }[] {
return [
{
command: this.BASE_COMMAND,
description: "Transfer tokens to different wallets",
usage: `${this.BASE_COMMAND} <help|list|token_account recipient_address amount>`
},
{
command: `${this.BASE_COMMAND} help`,
description: "Show available commands for token transfers",
usage: `${this.BASE_COMMAND} help`
},
{
command: `${this.BASE_COMMAND} list`,
description: "List all token accounts with balances",
usage: `${this.BASE_COMMAND} list`
},
{
command: `${this.BASE_COMMAND} transfer`,
description: "Transfer tokens to another wallet",
usage: `${this.BASE_COMMAND} <token_account> <recipient_address> <amount>`
}
];
}
}
export default TransferTokenPlugin;