diff --git a/src/actions/solana/verify.ts b/src/actions/solana/verify.ts new file mode 100644 index 00000000..a815cd3b --- /dev/null +++ b/src/actions/solana/verify.ts @@ -0,0 +1,183 @@ +import { Action } from "../../types/action"; +import { z } from "zod"; +import { getProgramVerificationStatus, verifySolanaProgram } from "../../tools"; + +export const verifyProgramAction: Action = { + name: "VERIFY_PROGRAM", + similes: [ + "verify program", + "check program", + "validate program", + "verify solana program", + "check contract", + "verify smart contract", + ], + description: `Verify a Solana program by comparing its on-chain bytecode with the source code from a GitHub repository.`, + examples: [ + [ + { + input: { + programId: "FWEYpBAf9WsemQiNbAewhyESfR38GBBHLrCaU3MpEKWv", + github: "https://github.com/solana-developers/verified-program", + commit: "5b82b86f02afbde330dff3e1847bed2d42069f4e", + }, + output: { + status: "success", + message: "Program verification completed", + details: { + programId: "FWEYpBAf9WsemQiNbAewhyESfR38GBBHLrCaU3MpEKWv", + repository: "https://github.com/solana-developers/verified-program", + commit: "5b82b86f02afbde330dff3e1847bed2d42069f4e", + verificationResult: { + verified: true, + message: "Program verified successfully", + }, + }, + }, + explanation: "Verify the example program with specific commit hash", + }, + ], + [ + { + input: { + programId: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + github: + "https://github.com/solana-labs/solana-program-library/tree/070934ae4f2975d602caa6bd1e88b2c010e4cab5", + commit: "070934ae4f2975d602caa6bd1e88b2c010e4cab5", + }, + output: { + status: "success", + message: "Program verification completed", + details: { + programId: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + repository: "https://github.com/solana-labs/solana-program-library", + commit: "070934ae4f2975d602caa6bd1e88b2c010e4cab5", + verificationResult: { + verified: true, + message: "Token program verified successfully", + }, + }, + }, + explanation: "Verify the Solana Token program from SPL", + }, + ], + ], + schema: z.object({ + programId: z.string().min(32, "Invalid Solana program ID"), + github: z.string().url("Invalid GitHub repository URL"), + commit: z.string().optional(), + }), + handler: async (input: Record) => { + try { + // Clean up GitHub URL if it contains tree/branch information + const githubUrl = input.github.split("/tree/")[0]; + + const verificationResult = await verifySolanaProgram( + githubUrl, + input.programId, + input.commit, + ); + + const parsedResult = JSON.parse(verificationResult); + + return { + status: "success", + message: "Program verification completed", + details: { + programId: input.programId, + repository: githubUrl, + commit: input.commit || "latest", + verificationResult: parsedResult, + }, + }; + } catch (error: any) { + return { + status: "error", + message: error.message, + code: "VERIFICATION_ERROR", + details: { + programId: input.programId, + repository: input.github, + error: error.stack, + }, + }; + } + }, +}; + +export const checkVerificationStatusAction: Action = { + name: "CHECK_VERIFICATION_STATUS", + similes: [ + "check program status", + "verify program status", + "check verification", + "get program verification", + "is program verified", + "program verification status", + ], + description: `Check the verification status of a Solana program using its program ID.`, + examples: [ + [ + { + input: { + programId: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + }, + output: { + status: "success", + message: "Verification status retrieved successfully", + details: { + programId: "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + verified: true, + repository: "https://github.com/solana-labs/solana-program-library", + commit: "070934ae4f2975d602caa6bd1e88b2c010e4cab5", + }, + }, + explanation: "Check verification status of the Solana Token program", + }, + ], + [ + { + input: { + programId: "FWEYpBAf9WsemQiNbAewhyESfR38GBBHLrCaU3MpEKWv", + }, + output: { + status: "success", + message: "Verification status retrieved successfully", + details: { + programId: "FWEYpBAf9WsemQiNbAewhyESfR38GBBHLrCaU3MpEKWv", + verified: true, + repository: "https://github.com/solana-developers/verified-program", + commit: "5b82b86f02afbde330dff3e1847bed2d42069f4e", + }, + }, + explanation: "Check verification status of an example verified program", + }, + ], + ], + schema: z.object({ + programId: z.string().min(32, "Invalid Solana program ID"), + }), + handler: async (input: Record) => { + try { + const verificationStatus = await getProgramVerificationStatus( + input.programId, + ); + + return { + status: "success", + message: "Verification status retrieved successfully", + verificationStatus, + }; + } catch (error: any) { + return { + status: "error", + message: error.message, + code: "STATUS_CHECK_ERROR", + details: { + programId: input.programId, + error: error.stack, + }, + }; + } + }, +}; diff --git a/src/agent/index.ts b/src/agent/index.ts index d087931c..153066a9 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -98,6 +98,8 @@ import { withdrawFromDriftVault, updateVaultDelegate, get_token_balance, + verifySolanaProgram, + getProgramVerificationStatus, } from "../tools"; import { Config, @@ -821,4 +823,10 @@ export class SolanaAgentKit { async updateDriftVaultDelegate(vaultAddress: string, delegate: string) { return await updateVaultDelegate(this, vaultAddress, delegate); } + async verifySolanaProgram(progamId: string, github: string, commit?: string) { + return await verifySolanaProgram(progamId, github, commit); + } + async getProgramVerificationStatus(programId: string) { + return await getProgramVerificationStatus(programId); + } } diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 809c3400..055e193a 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -114,7 +114,9 @@ import { SolanaUpdateDriftVaultTool, SolanaWithdrawFromDriftAccountTool, SolanaWithdrawFromDriftVaultTool, + SolanaProgramVerificationTool, } from "./index"; +import { SolanaProgramVerificationStatusTool } from "./solana"; export function createSolanaTools(solanaKit: SolanaAgentKit) { return [ @@ -208,5 +210,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaDriftVaultInfoTool(solanaKit), new SolanaWithdrawFromDriftAccountTool(solanaKit), new SolanaWithdrawFromDriftVaultTool(solanaKit), + new SolanaProgramVerificationTool(solanaKit), + new SolanaProgramVerificationStatusTool(solanaKit), ]; } diff --git a/src/langchain/solana/index.ts b/src/langchain/solana/index.ts index 02db35c3..e1828749 100644 --- a/src/langchain/solana/index.ts +++ b/src/langchain/solana/index.ts @@ -4,3 +4,4 @@ export * from "./balance"; export * from "./balance_other"; export * from "./close_empty_accounts"; export * from "./transfer"; +export * from "./verify"; diff --git a/src/langchain/solana/verify.ts b/src/langchain/solana/verify.ts new file mode 100644 index 00000000..5d23ad05 --- /dev/null +++ b/src/langchain/solana/verify.ts @@ -0,0 +1,102 @@ +import { PublicKey } from "@solana/web3.js"; +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaProgramVerificationTool extends Tool { + name = "verify_solana_program"; + description = `Verify a Solana program using its GitHub repository and program ID. + Input should be a JSON string with the following format: + { + "programId": string (required) - Solana program ID to verify, + "github": string (required) - GitHub repository URL, + "commit": string (optional) - Specific commit hash to verify + }`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + + if (!parsedInput.github || !parsedInput.programId) { + throw new Error( + "Missing required fields: 'github' and 'programId' are required", + ); + } + + const program_id = new PublicKey(parsedInput.programId); + if (!PublicKey.isOnCurve(program_id)) { + throw new Error("Invalid program ID"); + } + + const data = await this.solanaKit.verifySolanaProgram( + parsedInput.programId, + parsedInput.github, + parsedInput.commit, + ); + + return JSON.stringify({ + status: "success", + details: { + programId: parsedInput.programId, + repository: parsedInput.github, + commit: parsedInput.commit || "latest", + verificationResult: data, + }, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "VERIFICATION_ERROR", + details: error.stack, + }); + } + } +} + +export class SolanaProgramVerificationStatusTool extends Tool { + name = "check_program_verification_status"; + description = `Check if a Solana program is verified or not. + Input should be a JSON string with the following format: + { + "programId": string (required) - Solana program ID to check + }`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + + if (!parsedInput.programId) { + throw new Error("Missing required field: 'programId' is required"); + } + + const program_id = new PublicKey(parsedInput.programId); + if (!PublicKey.isOnCurve(program_id)) { + throw new Error("Invalid program ID"); + } + + const data = await this.solanaKit.getProgramVerificationStatus( + parsedInput.programId, + ); + + return JSON.stringify({ + status: "success", + message: "Verification status retrieved", + data, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: "STATUS_CHECK_ERROR", + }); + } + } +} diff --git a/src/tools/solana/index.ts b/src/tools/solana/index.ts index a7b6de96..fb983f4b 100644 --- a/src/tools/solana/index.ts +++ b/src/tools/solana/index.ts @@ -5,3 +5,4 @@ export * from "./transfer"; export * from "./get_balance"; export * from "./get_balance_other"; export * from "./get_token_balances"; +export * from "./verify"; diff --git a/src/tools/solana/verify.ts b/src/tools/solana/verify.ts new file mode 100644 index 00000000..dfca7c93 --- /dev/null +++ b/src/tools/solana/verify.ts @@ -0,0 +1,53 @@ +/** + * Verifies a Solana program using the given GitHub repository and program ID. + * + * @param {string} github - The GitHub repository URL. + * @param {string} program_id - The Solana program ID. + * @param {string} [commit] - The specific commit hash to verify (optional). + * @returns {Promise} - A promise that resolves to the verification response data. + * @throws {Error} - Throws an error if the program ID is invalid or if the verification request fails. + */ +export async function verifySolanaProgram( + program_id: string, + github: string, + commit?: string, +): Promise { + try { + const verifyResponse = await fetch("https://verify.osec.io/verify", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + program_id, + repository: github, + commit_hash: commit ? commit : "latest", + }), + }); + + const data = await verifyResponse.json(); + + return JSON.stringify(data); + } catch (err: any) { + throw new Error("Error in verifySolanaProgram: " + JSON.stringify(err)); + } +} + +/** + * Check if a solana program is verified or not + * @async + * @param {string} programId - The Solana program ID. + */ +export async function getProgramVerificationStatus(programId: string) { + try { + const res = await fetch(`https://verify.osec.io/status/${programId}`); + + const status = await res.json(); + + return status; + } catch (err: any) { + throw new Error( + "Error in getProgramVerificationStatus: " + JSON.stringify(err), + ); + } +}