chore: resolve merge conflights

This commit is contained in:
jakeyvee
2025-01-16 15:21:20 +08:00
26 changed files with 866 additions and 4 deletions

View File

@@ -14,6 +14,9 @@ jobs:
with:
version: 9.4.0
- name: Install node-gyp prerequisites
run: sudo apt update && sudo apt install -y build-essential python3 pkg-config libudev-dev libusb-1.0-0-dev
- uses: actions/setup-node@v4
with:
node-version: "23"
@@ -21,7 +24,7 @@ jobs:
- name: Install dependencies
run: pnpm install -r --no-frozen-lockfile
- name: Run lint and fix
run: pnpm run lint:fix

View File

@@ -1,3 +1,4 @@
import tokenBalancesAction from "./tokenBalances";
import deployTokenAction from "./metaplex/deployToken";
import balanceAction from "./solana/balance";
import transferAction from "./solana/transfer";
@@ -64,6 +65,7 @@ import withdrawVoltrStrategyAction from "./voltr/withdrawStrategy";
export const ACTIONS = {
WALLET_ADDRESS_ACTION: getWalletAddressAction,
TOKEN_BALANCES_ACTION: tokenBalancesAction,
DEPLOY_TOKEN_ACTION: deployTokenAction,
BALANCE_ACTION: balanceAction,
TRANSFER_ACTION: transferAction,

View File

@@ -1,6 +1,6 @@
import { PublicKey } from "@solana/web3.js";
import { Action } from "../../types/action";
import { SolanaAgentKit } from "../../agent";
import type { Action } from "../../types/action";
import type { SolanaAgentKit } from "../../agent";
import { z } from "zod";
import { get_balance } from "../../tools";

View File

@@ -0,0 +1,80 @@
import { PublicKey } from "@solana/web3.js";
import type { Action } from "../types/action";
import type { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { get_token_balance } from "../tools";
const tokenBalancesAction: Action = {
name: "TOKEN_BALANCE_ACTION",
similes: [
"check token balances",
"get wallet token balances",
"view token balances",
"show token balances",
"check token balance",
],
description: `Get the token balances of a Solana wallet.
If you want to get the balance of your wallet, you don't need to provide the wallet address.`,
examples: [
[
{
input: {},
output: {
status: "success",
balance: {
sol: 100,
tokens: [
{
tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
name: "USD Coin",
symbol: "USDC",
balance: 100,
decimals: 9,
},
],
},
},
explanation: "Get token balances of the wallet",
},
],
[
{
input: {
walletAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
},
output: {
status: "success",
balance: {
sol: 100,
tokens: [
{
tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
name: "USD Coin",
symbol: "USDC",
balance: 100,
decimals: 9,
},
],
},
},
explanation: "Get address token balance",
},
],
],
schema: z.object({
walletAddress: z.string().optional(),
}),
handler: async (agent: SolanaAgentKit, input) => {
const balance = await get_token_balance(
agent,
input.tokenAddress && new PublicKey(input.tokenAddress),
);
return {
status: "success",
balance: balance,
};
},
};
export default tokenBalancesAction;

View File

@@ -97,6 +97,7 @@ import {
withdrawFromDriftUserAccount,
withdrawFromDriftVault,
updateVaultDelegate,
get_token_balance,
voltrGetPositionValues,
voltrDepositStrategy,
voltrWithdrawStrategy,
@@ -192,6 +193,19 @@ export class SolanaAgentKit {
return get_balance(this, token_address);
}
async getTokenBalances(wallet_address?: PublicKey): Promise<{
sol: number;
tokens: Array<{
tokenAddress: string;
name: string;
symbol: string;
balance: number;
decimals: number;
}>;
}> {
return get_token_balance(this, wallet_address);
}
async getBalanceOther(
walletAddress: PublicKey,
tokenAddress?: PublicKey,

View File

@@ -0,0 +1,38 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaCreateDriftUserAccountTool extends Tool {
name = "create_drift_user_account";
description = `Create a new user account with a deposit on Drift protocol.
Inputs (JSON string):
- amount: number, amount of the token to deposit (required)
- symbol: string, symbol of the token to deposit (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const res = await this.solanaKit.createDriftUserAccount(
parsedInput.amount,
parsedInput.symbol,
);
return JSON.stringify({
status: "success",
message: `User account created with ${parsedInput.amount} ${parsedInput.symbol} successfully deposited`,
account: res.account,
signature: res.txSignature,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "CREATE_DRIFT_USER_ACCOUNT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,42 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaCreateDriftVaultTool extends Tool {
name = "create_drift_vault";
description = `Create a new drift vault delegating the agents address as the owner.
Inputs (JSON string):
- name: string, unique vault name (min 5 chars)
- marketName: string, market name in TOKEN-SPOT format
- redeemPeriod: number, days to wait before funds can be redeemed (min 1)
- maxTokens: number, maximum tokens vault can accommodate (min 100)
- minDepositAmount: number, minimum deposit amount
- managementFee: number, fee percentage for managing funds (max 20)
- profitShare: number, profit sharing percentage (max 90, default 5)
- hurdleRate: number, optional hurdle rate
- permissioned: boolean, whether vault has whitelist`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.createDriftVault(parsedInput);
return JSON.stringify({
status: "success",
message: "Drift vault created successfully",
vaultName: parsedInput.name,
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "CREATE_DRIFT_VAULT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,37 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaDepositIntoDriftVaultTool extends Tool {
name = "deposit_into_drift_vault";
description = `Deposit funds into an existing drift vault.
Inputs (JSON string):
- vaultAddress: string, address of the vault (required)
- amount: number, amount to deposit (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.depositIntoDriftVault(
parsedInput.amount,
parsedInput.vaultAddress,
);
return JSON.stringify({
status: "success",
message: "Funds deposited successfully",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DEPOSIT_INTO_VAULT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,39 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaDepositToDriftUserAccountTool extends Tool {
name = "deposit_to_drift_user_account";
description = `Deposit funds into your drift user account.
Inputs (JSON string):
- amount: number, amount to deposit (required)
- symbol: string, token symbol (required)
- repay: boolean, whether to repay borrowed funds (optional, default: false)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.depositToDriftUserAccount(
parsedInput.amount,
parsedInput.symbol,
parsedInput.repay,
);
return JSON.stringify({
status: "success",
message: "Funds deposited successfully",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DEPOSIT_TO_DRIFT_ACCOUNT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,32 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaDeriveVaultAddressTool extends Tool {
name = "derive_drift_vault_address";
description = `Derive a drift vault address from the vault's name.
Inputs (JSON string):
- name: string, name of the vault to derive the address of (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const address = await this.solanaKit.deriveDriftVaultAddress(input);
return JSON.stringify({
status: "success",
message: "Vault address derived successfully",
address,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DERIVE_VAULT_ADDRESS_ERROR",
});
}
}
}

View File

@@ -0,0 +1,38 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaCheckDriftAccountTool extends Tool {
name = "does_user_have_drift_account";
description = `Check if a user has a Drift account.
Inputs: No inputs required - checks the current user's account`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(_input: string): Promise<string> {
try {
const res = await this.solanaKit.doesUserHaveDriftAccount();
if (!res.hasAccount) {
return JSON.stringify({
status: "error",
message: "You do not have a Drift account",
});
}
return JSON.stringify({
status: "success",
message: "Nice! You have a Drift account",
account: res.account,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "CHECK_DRIFT_ACCOUNT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,29 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaDriftUserAccountInfoTool extends Tool {
name = "drift_user_account_info";
description = `Get information about your drift account.
Inputs: No inputs required - retrieves current user's account info`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(_input: string): Promise<string> {
try {
const accountInfo = await this.solanaKit.driftUserAccountInfo();
return JSON.stringify({
status: "success",
data: accountInfo,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DRIFT_ACCOUNT_INFO_ERROR",
});
}
}
}

View File

@@ -0,0 +1,15 @@
export * from "./create_user_account";
export * from "./create_vault";
export * from "./deposit_into_vault";
export * from "./deposit_to_user_account";
export * from "./derive_vault_address";
export * from "./does_user_have_drift_account";
export * from "./drift_user_account_info";
export * from "./request_withdrawal";
export * from "./trade_delegated_vault";
export * from "./trade_perp_account";
export * from "./update_drift_vault_delegate";
export * from "./update_vault";
export * from "./vault_info";
export * from "./withdraw_from_account";
export * from "./withdraw_from_vault";

View File

@@ -0,0 +1,37 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaRequestDriftWithdrawalTool extends Tool {
name = "request_withdrawal_from_drift_vault";
description = `Request a withdrawal from an existing drift vault.
Inputs (JSON string):
- vaultAddress: string, vault address (required)
- amount: number, amount of shares to withdraw (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.requestWithdrawalFromDriftVault(
parsedInput.amount,
parsedInput.vaultAddress,
);
return JSON.stringify({
status: "success",
message: "Withdrawal request successful",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "REQUEST_DRIFT_WITHDRAWAL_ERROR",
});
}
}
}

View File

@@ -0,0 +1,49 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaTradeDelegatedDriftVaultTool extends Tool {
name = "trade_delegated_drift_vault";
description = `Carry out trades in a Drift vault.
Inputs (JSON string):
- vaultAddress: string, address of the Drift vault
- amount: number, amount to trade
- symbol: string, symbol of the token to trade
- action: "long" | "short", trade direction
- type: "market" | "limit", order type
- price: number, optional limit price`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.tradeUsingDelegatedDriftVault(
parsedInput.vaultAddress,
parsedInput.amount,
parsedInput.symbol,
parsedInput.action,
parsedInput.type,
parsedInput.price,
);
return JSON.stringify({
status: "success",
message:
parsedInput.type === "limit"
? "Order placed successfully"
: "Trade successful",
transactionId: tx,
...parsedInput,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "TRADE_DRIFT_VAULT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,42 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaTradeDriftPerpAccountTool extends Tool {
name = "trade_drift_perp_account";
description = `Trade a perpetual account on Drift protocol.
Inputs (JSON string):
- amount: number, amount to trade (required)
- symbol: string, token symbol (required)
- action: "long" | "short", trade direction (required)
- type: "market" | "limit", order type (required)
- price: number, required for limit orders`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const signature = await this.solanaKit.tradeUsingDriftPerpAccount(
parsedInput.amount,
parsedInput.symbol,
parsedInput.action,
parsedInput.type,
parsedInput.price,
);
return JSON.stringify({
status: "success",
signature,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "TRADE_PERP_ACCOUNT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,37 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaUpdateDriftVaultDelegateTool extends Tool {
name = "update_drift_vault_delegate";
description = `Update the delegate of a drift vault.
Inputs (JSON string):
- vaultAddress: string, address of the vault (required)
- newDelegate: string, address of the new delegate (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.updateDriftVaultDelegate(
parsedInput.vaultAddress,
parsedInput.newDelegate,
);
return JSON.stringify({
status: "success",
message: "Vault delegate updated successfully",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UPDATE_DRIFT_VAULT_DELEGATE_ERROR",
});
}
}
}

View File

@@ -0,0 +1,52 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaUpdateDriftVaultTool extends Tool {
name = "update_drift_vault";
description = `Update an existing drift vault with new settings.
Inputs (JSON string):
- vaultAddress: string, vault address (required)
- redeemPeriod: number, days until redemption (optional)
- maxTokens: number, maximum tokens allowed (optional)
- minDepositAmount: number, minimum deposit amount (optional)
- managementFee: number, management fee percentage (optional)
- profitShare: number, profit sharing percentage (optional)
- hurdleRate: number, hurdle rate (optional)
- permissioned: boolean, whitelist requirement (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.updateDriftVault(
parsedInput.vaultAddress,
// @ts-expect-error - type mismatch
{
hurdleRate: parsedInput.hurdleRate,
maxTokens: parsedInput.maxTokens,
minDepositAmount: parsedInput.minDepositAmount,
profitShare: parsedInput.profitShare,
managementFee: parsedInput.managementFee,
permissioned: parsedInput.permissioned,
redeemPeriod: parsedInput.redeemPeriod,
},
);
return JSON.stringify({
status: "success",
message: "Drift vault parameters updated successfully",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UPDATE_DRIFT_VAULT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,32 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaDriftVaultInfoTool extends Tool {
name = "drift_vault_info";
description = `Get information about a drift vault.
Inputs (JSON string):
- vaultNameOrAddress: string, name or address of the vault (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const vaultInfo = await this.solanaKit.getDriftVaultInfo(input);
return JSON.stringify({
status: "success",
message: "Vault info retrieved successfully",
data: vaultInfo,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "DRIFT_VAULT_INFO_ERROR",
});
}
}
}

View File

@@ -0,0 +1,39 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaWithdrawFromDriftAccountTool extends Tool {
name = "withdraw_from_drift_account";
description = `Withdraw or borrow funds from your drift account.
Inputs (JSON string):
- amount: number, amount to withdraw (required)
- symbol: string, token symbol (required)
- isBorrow: boolean, whether to borrow funds instead of withdrawing (optional, default: false)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.withdrawFromDriftAccount(
parsedInput.amount,
parsedInput.symbol,
parsedInput.isBorrow,
);
return JSON.stringify({
status: "success",
message: "Funds withdrawn successfully",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "WITHDRAW_FROM_DRIFT_ACCOUNT_ERROR",
});
}
}
}

View File

@@ -0,0 +1,32 @@
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../../agent";
export class SolanaWithdrawFromDriftVaultTool extends Tool {
name = "withdraw_from_drift_vault";
description = `Withdraw funds from a vault given the redemption time has elapsed.
Inputs (JSON string):
- vaultAddress: string, vault address (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const tx = await this.solanaKit.withdrawFromDriftVault(input);
return JSON.stringify({
status: "success",
message: "Withdrawal successful",
signature: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "WITHDRAW_FROM_DRIFT_VAULT_ERROR",
});
}
}
}

View File

@@ -25,6 +25,7 @@ export * from "./sns";
export * from "./lightprotocol";
export * from "./squads";
export * from "./helius";
export * from "./drift";
export * from "./voltr";
import { SolanaAgentKit } from "../agent";
@@ -99,6 +100,21 @@ import {
SolanaDeleteHeliusWebhookTool,
SolanaParseTransactionHeliusTool,
SolanaGetAllAssetsByOwner,
SolanaCheckDriftAccountTool,
SolanaCreateDriftUserAccountTool,
SolanaCreateDriftVaultTool,
SolanaDepositIntoDriftVaultTool,
SolanaDepositToDriftUserAccountTool,
SolanaDeriveVaultAddressTool,
SolanaDriftUserAccountInfoTool,
SolanaDriftVaultInfoTool,
SolanaRequestDriftWithdrawalTool,
SolanaTradeDelegatedDriftVaultTool,
SolanaTradeDriftPerpAccountTool,
SolanaUpdateDriftVaultDelegateTool,
SolanaUpdateDriftVaultTool,
SolanaWithdrawFromDriftAccountTool,
SolanaWithdrawFromDriftVaultTool,
SolanaVoltrGetPositionValues,
SolanaVoltrDepositStrategy,
SolanaVoltrWithdrawStrategy,
@@ -181,6 +197,21 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaHeliusWebhookTool(solanaKit),
new SolanaGetHeliusWebhookTool(solanaKit),
new SolanaDeleteHeliusWebhookTool(solanaKit),
new SolanaCreateDriftUserAccountTool(solanaKit),
new SolanaCreateDriftVaultTool(solanaKit),
new SolanaDepositIntoDriftVaultTool(solanaKit),
new SolanaDepositToDriftUserAccountTool(solanaKit),
new SolanaDeriveVaultAddressTool(solanaKit),
new SolanaCheckDriftAccountTool(solanaKit),
new SolanaDriftUserAccountInfoTool(solanaKit),
new SolanaRequestDriftWithdrawalTool(solanaKit),
new SolanaTradeDelegatedDriftVaultTool(solanaKit),
new SolanaTradeDriftPerpAccountTool(solanaKit),
new SolanaUpdateDriftVaultDelegateTool(solanaKit),
new SolanaUpdateDriftVaultTool(solanaKit),
new SolanaDriftVaultInfoTool(solanaKit),
new SolanaWithdrawFromDriftAccountTool(solanaKit),
new SolanaWithdrawFromDriftVaultTool(solanaKit),
new SolanaVoltrGetPositionValues(solanaKit),
new SolanaVoltrDepositStrategy(solanaKit),
new SolanaVoltrWithdrawStrategy(solanaKit),

View File

@@ -0,0 +1,59 @@
import { LAMPORTS_PER_SOL, type PublicKey } from "@solana/web3.js";
import type { SolanaAgentKit } from "../../index";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { getTokenMetadata } from "../../utils/tokenMetadata";
/**
* Get the token balances of a Solana wallet
* @param agent - SolanaAgentKit instance
* @param token_address - Optional SPL token mint address. If not provided, returns SOL balance
* @returns Promise resolving to the balance as an object containing sol balance and token balances with their respective mints, symbols, names and decimals
*/
export async function get_token_balance(
agent: SolanaAgentKit,
walletAddress?: PublicKey,
): Promise<{
sol: number;
tokens: Array<{
tokenAddress: string;
name: string;
symbol: string;
balance: number;
decimals: number;
}>;
}> {
const [lamportsBalance, tokenAccountData] = await Promise.all([
agent.connection.getBalance(walletAddress ?? agent.wallet_address),
agent.connection.getParsedTokenAccountsByOwner(
walletAddress ?? agent.wallet_address,
{
programId: TOKEN_PROGRAM_ID,
},
),
]);
const removedZeroBalance = tokenAccountData.value.filter(
(v) => v.account.data.parsed.info.tokenAmount.uiAmount !== 0,
);
const tokenBalances = await Promise.all(
removedZeroBalance.map(async (v) => {
const mint = v.account.data.parsed.info.mint;
const mintInfo = await getTokenMetadata(agent.connection, mint);
return {
tokenAddress: mint,
name: mintInfo.name ?? "",
symbol: mintInfo.symbol ?? "",
balance: v.account.data.parsed.info.tokenAmount.uiAmount as number,
decimals: v.account.data.parsed.info.tokenAmount.decimals as number,
};
}),
);
const solBalance = lamportsBalance / LAMPORTS_PER_SOL;
return {
sol: solBalance,
tokens: tokenBalances,
};
}

View File

@@ -4,3 +4,4 @@ export * from "./close_empty_token_accounts";
export * from "./transfer";
export * from "./get_balance";
export * from "./get_balance_other";
export * from "./get_token_balances";

View File

@@ -0,0 +1,83 @@
import { Connection, PublicKey } from "@solana/web3.js";
export async function getTokenMetadata(
connection: Connection,
tokenMint: string,
) {
const METADATA_PROGRAM_ID = new PublicKey(
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s",
);
const [metadataPDA] = PublicKey.findProgramAddressSync(
[
Buffer.from("metadata"),
METADATA_PROGRAM_ID.toBuffer(),
new PublicKey(tokenMint).toBuffer(),
],
METADATA_PROGRAM_ID,
);
const metadata = await connection.getAccountInfo(metadataPDA);
if (!metadata?.data) {
throw new Error("Metadata not found");
}
let offset = 1 + 32 + 32; // key + update auth + mint
const data = metadata.data;
const decoder = new TextDecoder();
// Read variable length strings
const readString = () => {
let nameLength = data[offset];
while (nameLength === 0) {
offset++;
nameLength = data[offset];
if (offset >= data.length) {
return null;
}
}
offset++;
const name = decoder
.decode(data.slice(offset, offset + nameLength))
// @eslint-disable-next-line no-control-regex
.replace(new RegExp(String.fromCharCode(0), "g"), "");
offset += nameLength;
return name;
};
const name = readString();
const symbol = readString();
const uri = readString();
// Read remaining data
const sellerFeeBasisPoints = data.readUInt16LE(offset);
offset += 2;
let creators:
| { address: PublicKey; verified: boolean; share: number }[]
| null = null;
if (data[offset] === 1) {
offset++;
const numCreators = data[offset];
offset++;
creators = [...Array(numCreators)].map(() => {
const creator = {
address: new PublicKey(data.slice(offset, offset + 32)),
verified: data[offset + 32] === 1,
share: data[offset + 33],
};
offset += 34;
return creator;
});
}
return {
name,
symbol,
uri,
sellerFeeBasisPoints,
creators,
};
}

View File

@@ -95,7 +95,6 @@ async function runChatMode() {
);
const tools = createVercelAITools(solanaAgent);
console.log(tools);
const rl = readline.createInterface({
input: process.stdin,