chore: lint

This commit is contained in:
aryan
2024-12-30 07:04:06 +05:30
parent 98369a0912
commit 46ff233f35
4 changed files with 221 additions and 210 deletions

View File

@@ -8,27 +8,30 @@ import * as readline from "readline";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
dotenv.config();
const checkpointer = PostgresSaver.fromConnString(
process.env.POSTGRES_DB_URL!
);
const checkpointer = PostgresSaver.fromConnString(process.env.POSTGRES_DB_URL!);
function validateEnvironment(): void {
const missingVars: string[] = [];
const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY", "POSTGRES_DB_URL"];
const missingVars: string[] = [];
const requiredVars = [
"OPENAI_API_KEY",
"RPC_URL",
"SOLANA_PRIVATE_KEY",
"POSTGRES_DB_URL",
];
requiredVars.forEach((varName) => {
if (!process.env[varName]) {
missingVars.push(varName);
}
});
if (missingVars.length > 0) {
console.error("Error: Required environment variables are not set");
missingVars.forEach((varName) => {
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
});
process.exit(1);
requiredVars.forEach((varName) => {
if (!process.env[varName]) {
missingVars.push(varName);
}
});
if (missingVars.length > 0) {
console.error("Error: Required environment variables are not set");
missingVars.forEach((varName) => {
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
});
process.exit(1);
}
}
validateEnvironment();
@@ -36,37 +39,37 @@ validateEnvironment();
const WALLET_DATA_FILE = "wallet_data.txt";
async function initializeAgent() {
try {
const llm = new ChatOpenAI({
modelName: "gpt-4o-mini",
temperature: 0.7,
});
try {
const llm = new ChatOpenAI({
modelName: "gpt-4o-mini",
temperature: 0.7,
});
let walletDataStr: string | null = null;
let walletDataStr: string | null = null;
if (fs.existsSync(WALLET_DATA_FILE)) {
try {
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
} catch (error) {
console.error("Error reading wallet data:", error);
}
}
if (fs.existsSync(WALLET_DATA_FILE)) {
try {
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
} catch (error) {
console.error("Error reading wallet data:", error);
}
}
const solanaAgent = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL,
process.env.OPENAI_API_KEY!,
);
const solanaAgent = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL,
process.env.OPENAI_API_KEY!,
);
const tools = createSolanaTools(solanaAgent);
await checkpointer.setup();
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
const tools = createSolanaTools(solanaAgent);
await checkpointer.setup();
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
const agent = createReactAgent({
llm,
tools,
checkpointSaver: checkpointer,
messageModifier: `
const agent = createReactAgent({
llm,
tools,
checkpointSaver: checkpointer,
messageModifier: `
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
@@ -75,146 +78,146 @@ async function initializeAgent() {
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
`,
});
});
if (walletDataStr) {
fs.writeFileSync(WALLET_DATA_FILE, walletDataStr);
}
return { agent, config };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
if (walletDataStr) {
fs.writeFileSync(WALLET_DATA_FILE, walletDataStr);
}
return { agent, config };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
}
}
async function runAutonomousMode(agent: any, config: any, interval = 10) {
console.log("Starting autonomous mode...");
console.log("Starting autonomous mode...");
while (true) {
try {
const thought =
"Be creative and do something interesting on the blockchain. " +
"Choose an action or set of actions and execute it that highlights your abilities.";
while (true) {
try {
const thought =
"Be creative and do something interesting on the blockchain. " +
"Choose an action or set of actions and execute it that highlights your abilities.";
const stream = await agent.stream(
{ messages: [new HumanMessage(thought)] },
config,
);
const stream = await agent.stream(
{ messages: [new HumanMessage(thought)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
}
async function runChatMode(agent: any, config: any) {
console.log("Starting chat mode... Type 'exit' to end.");
console.log("Starting chat mode... Type 'exit' to end.");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
try {
while (true) {
const userInput = await question("\nPrompt: ");
try {
while (true) {
const userInput = await question("\nPrompt: ");
if (userInput.toLowerCase() === "exit") {
break;
}
if (userInput.toLowerCase() === "exit") {
break;
}
const stream = await agent.stream(
{ messages: [new HumanMessage(userInput)] },
config,
);
const stream = await agent.stream(
{ messages: [new HumanMessage(userInput)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
} finally {
rl.close();
console.log("-------------------");
}
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
} finally {
rl.close();
}
}
async function chooseMode(): Promise<"chat" | "auto"> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
while (true) {
console.log("\nAvailable modes:");
console.log("1. chat - Interactive chat mode");
console.log("2. auto - Autonomous action mode");
while (true) {
console.log("\nAvailable modes:");
console.log("1. chat - Interactive chat mode");
console.log("2. auto - Autonomous action mode");
const choice = (await question("\nChoose a mode (enter number or name): "))
.toLowerCase()
.trim();
const choice = (await question("\nChoose a mode (enter number or name): "))
.toLowerCase()
.trim();
rl.close();
rl.close();
if (choice === "1" || choice === "chat") {
return "chat";
} else if (choice === "2" || choice === "auto") {
return "auto";
}
console.log("Invalid choice. Please try again.");
if (choice === "1" || choice === "chat") {
return "chat";
} else if (choice === "2" || choice === "auto") {
return "auto";
}
console.log("Invalid choice. Please try again.");
}
}
async function main() {
try {
console.log("Starting Agent...");
const { agent, config } = await initializeAgent();
const mode = await chooseMode();
try {
console.log("Starting Agent...");
const { agent, config } = await initializeAgent();
const mode = await chooseMode();
if (mode === "chat") {
await runChatMode(agent, config);
} else {
await runAutonomousMode(agent, config);
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
if (mode === "chat") {
await runChatMode(agent, config);
} else {
await runAutonomousMode(agent, config);
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
if (require.main === module) {
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
}

View File

@@ -356,21 +356,15 @@ export class SolanaAgentKit {
async tensorListNFT(
nftMint: PublicKey,
price: number,
expirySeconds?: number
): Promise<string> {
return listNFTForSale(this, nftMint, price, expirySeconds);
return listNFTForSale(this, nftMint, price);
}
async tensorBuyNFT(
nftMint: PublicKey,
maxPrice: number
): Promise<string> {
async tensorBuyNFT(nftMint: PublicKey, maxPrice: number): Promise<string> {
return buyNFT(this, nftMint, maxPrice);
}
async tensorCancelListing(
nftMint: PublicKey
): Promise<string> {
async tensorCancelListing(nftMint: PublicKey): Promise<string> {
return cancelListing(this, nftMint);
}
}

View File

@@ -1332,8 +1332,7 @@ export class SolanaListNFTForSaleTool extends Tool {
Inputs (input is a JSON string):
nftMint: string, the mint address of the NFT (required)
price: number, price in SOL (required)
expirySeconds: number, expiry time in seconds (optional)`;
price: number, price in SOL (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
@@ -1342,25 +1341,26 @@ export class SolanaListNFTForSaleTool extends Tool {
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
// Validate NFT ownership first
const nftAccount = await this.solanaKit.connection.getTokenAccountsByOwner(
this.solanaKit.wallet_address,
{ mint: new PublicKey(parsedInput.nftMint) }
);
const nftAccount =
await this.solanaKit.connection.getTokenAccountsByOwner(
this.solanaKit.wallet_address,
{ mint: new PublicKey(parsedInput.nftMint) },
);
if (nftAccount.value.length === 0) {
return JSON.stringify({
status: "error",
message: "NFT not found in wallet. Please make sure you own this NFT.",
code: "NFT_NOT_FOUND"
message:
"NFT not found in wallet. Please make sure you own this NFT.",
code: "NFT_NOT_FOUND",
});
}
const tx = await this.solanaKit.tensorListNFT(
new PublicKey(parsedInput.nftMint),
parsedInput.price,
parsedInput.expirySeconds
);
return JSON.stringify({
@@ -1368,13 +1368,13 @@ export class SolanaListNFTForSaleTool extends Tool {
message: "NFT listed for sale successfully",
transaction: tx,
price: parsedInput.price,
nftMint: parsedInput.nftMint
nftMint: parsedInput.nftMint,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR"
code: error.code || "UNKNOWN_ERROR",
});
}
}
@@ -1395,10 +1395,10 @@ export class SolanaBuyNFTTool extends Tool {
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.tensorBuyNFT(
new PublicKey(parsedInput.nftMint),
parsedInput.maxPrice
parsedInput.maxPrice,
);
return JSON.stringify({
@@ -1406,13 +1406,13 @@ export class SolanaBuyNFTTool extends Tool {
message: "NFT purchased successfully",
transaction: tx,
maxPrice: parsedInput.maxPrice,
nftMint: parsedInput.nftMint
nftMint: parsedInput.nftMint,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR"
code: error.code || "UNKNOWN_ERROR",
});
}
}
@@ -1432,22 +1432,22 @@ export class SolanaCancelNFTListingTool extends Tool {
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.tensorCancelListing(
new PublicKey(parsedInput.nftMint)
new PublicKey(parsedInput.nftMint),
);
return JSON.stringify({
status: "success",
message: "NFT listing cancelled successfully",
transaction: tx,
nftMint: parsedInput.nftMint
nftMint: parsedInput.nftMint,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR"
code: error.code || "UNKNOWN_ERROR",
});
}
}

View File

@@ -3,17 +3,20 @@ import { TensorSwapSDK } from "@tensor-oss/tensorswap-sdk";
import { PublicKey, Transaction } from "@solana/web3.js";
import { AnchorProvider, Wallet } from "@coral-xyz/anchor";
import { BN } from "bn.js";
import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID,getAccount } from '@solana/spl-token';
import {
getAssociatedTokenAddress,
TOKEN_PROGRAM_ID,
getAccount,
} from "@solana/spl-token";
export async function listNFTForSale(
agent: SolanaAgentKit,
nftMint: PublicKey,
price: number,
expirySeconds?: number
): Promise<string> {
try {
if (!PublicKey.isOnCurve(nftMint)) {
throw new Error('Invalid NFT mint address');
throw new Error("Invalid NFT mint address");
}
const mintInfo = await agent.connection.getAccountInfo(nftMint);
@@ -21,33 +24,32 @@ export async function listNFTForSale(
throw new Error(`NFT mint ${nftMint.toString()} does not exist`);
}
const ata = await getAssociatedTokenAddress(
nftMint,
agent.wallet_address
);
const ata = await getAssociatedTokenAddress(nftMint, agent.wallet_address);
try {
const tokenAccount = await getAccount(
agent.connection,
ata
);
const tokenAccount = await getAccount(agent.connection, ata);
if (!tokenAccount || tokenAccount.amount <= 0) {
throw new Error(`You don't own this NFT (${nftMint.toString()})`);
}
} catch (e) {
throw new Error(`No token account found for mint ${nftMint.toString()}. Make sure you own this NFT.`);
throw new Error(
`No token account found for mint ${nftMint.toString()}. Make sure you own this NFT.`,
);
}
const provider = new AnchorProvider(
agent.connection,
new Wallet(agent.wallet),
AnchorProvider.defaultOptions()
AnchorProvider.defaultOptions(),
);
const tensorSwapSdk = new TensorSwapSDK({ provider });
const priceInLamports = new BN(price * 1e9);
const nftSource = await getAssociatedTokenAddress(nftMint, agent.wallet_address);
const nftSource = await getAssociatedTokenAddress(
nftMint,
agent.wallet_address,
);
const { tx } = await tensorSwapSdk.list({
nftMint,
@@ -55,12 +57,15 @@ export async function listNFTForSale(
owner: agent.wallet_address,
price: priceInLamports,
tokenProgram: TOKEN_PROGRAM_ID,
payer: agent.wallet_address
payer: agent.wallet_address,
});
const transaction = new Transaction();
transaction.add(...tx.ixs);
return await agent.connection.sendTransaction(transaction, [agent.wallet, ...tx.extraSigners]);
return await agent.connection.sendTransaction(transaction, [
agent.wallet,
...tx.extraSigners,
]);
} catch (error: any) {
console.error("Full error details:", error);
throw error;
@@ -70,17 +75,20 @@ export async function listNFTForSale(
export async function buyNFT(
agent: SolanaAgentKit,
nftMint: PublicKey,
maxPrice: number
maxPrice: number,
): Promise<string> {
const provider = new AnchorProvider(
agent.connection,
new Wallet(agent.wallet),
AnchorProvider.defaultOptions()
AnchorProvider.defaultOptions(),
);
const tensorSwapSdk = new TensorSwapSDK({ provider });
const maxPriceInLamports = new BN(maxPrice * 1e9);
const nftBuyerAcc = await getAssociatedTokenAddress(nftMint, agent.wallet_address);
const nftBuyerAcc = await getAssociatedTokenAddress(
nftMint,
agent.wallet_address,
);
const { tx } = await tensorSwapSdk.buySingleListingT22({
nftMint,
@@ -91,42 +99,48 @@ export async function buyNFT(
takerBroker: null,
compute: null,
priorityMicroLamports: null,
transferHook: null
transferHook: null,
});
const transaction = new Transaction();
transaction.add(...tx.ixs);
return await agent.connection.sendTransaction(transaction, [agent.wallet, ...tx.extraSigners]);
return await agent.connection.sendTransaction(transaction, [
agent.wallet,
...tx.extraSigners,
]);
}
export async function cancelListing(
agent: SolanaAgentKit,
nftMint: PublicKey
nftMint: PublicKey,
): Promise<string> {
const provider = new AnchorProvider(
agent.connection,
new Wallet(agent.wallet),
AnchorProvider.defaultOptions()
AnchorProvider.defaultOptions(),
);
const tensorSwapSdk = new TensorSwapSDK({ provider });
const nftDest = await getAssociatedTokenAddress(
nftMint,
nftMint,
agent.wallet_address,
false,
TOKEN_PROGRAM_ID
TOKEN_PROGRAM_ID,
);
const { tx } = await tensorSwapSdk.delist({
nftMint,
nftDest,
owner: agent.wallet_address,
tokenProgram: TOKEN_PROGRAM_ID,
payer: agent.wallet_address,
authData: null
authData: null,
});
const transaction = new Transaction();
transaction.add(...tx.ixs);
return await agent.connection.sendTransaction(transaction, [agent.wallet, ...tx.extraSigners]);
}
return await agent.connection.sendTransaction(transaction, [
agent.wallet,
...tx.extraSigners,
]);
}