Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add osec solana program verification api #220

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions src/actions/solana/verify.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>) => {
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<string, any>) => {
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,
},
};
}
},
};
8 changes: 8 additions & 0 deletions src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ import {
withdrawFromDriftVault,
updateVaultDelegate,
get_token_balance,
verifySolanaProgram,
getProgramVerificationStatus,
} from "../tools";
import {
Config,
Expand Down Expand Up @@ -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);
}
}
4 changes: 4 additions & 0 deletions src/langchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ import {
SolanaUpdateDriftVaultTool,
SolanaWithdrawFromDriftAccountTool,
SolanaWithdrawFromDriftVaultTool,
SolanaProgramVerificationTool,
} from "./index";
import { SolanaProgramVerificationStatusTool } from "./solana";

export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
Expand Down Expand Up @@ -208,5 +210,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaDriftVaultInfoTool(solanaKit),
new SolanaWithdrawFromDriftAccountTool(solanaKit),
new SolanaWithdrawFromDriftVaultTool(solanaKit),
new SolanaProgramVerificationTool(solanaKit),
new SolanaProgramVerificationStatusTool(solanaKit),
];
}
1 change: 1 addition & 0 deletions src/langchain/solana/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./balance";
export * from "./balance_other";
export * from "./close_empty_accounts";
export * from "./transfer";
export * from "./verify";
102 changes: 102 additions & 0 deletions src/langchain/solana/verify.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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<string> {
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",
});
}
}
}
1 change: 1 addition & 0 deletions src/tools/solana/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./transfer";
export * from "./get_balance";
export * from "./get_balance_other";
export * from "./get_token_balances";
export * from "./verify";
53 changes: 53 additions & 0 deletions src/tools/solana/verify.ts
Original file line number Diff line number Diff line change
@@ -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<string>} - 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<string> {
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),
);
}
}
Loading