Skip to main content
Glama
delegateTool.ts5.81 kB
import { StructuredTool } from "langchain/tools"; import { z } from "zod"; import { OraichainAgentKit } from "@oraichain/agent-kit"; import { Coin } from "@cosmjs/stargate"; import { makeSignBytes } from "@cosmjs/proto-signing"; /** * A tool for delegating ORAI tokens to a validator on the Oraichain network. * This tool creates and signs a delegation transaction that allows users to stake their tokens with a validator. * * @remarks * The tool uses the Cosmos SDK staking module's MsgDelegate message type. * The fee for the transaction is automatically calculated by the chain. * * @example * ```typescript * const input = { * delegatorAddress: "orai1...", * validatorAddress: "oraivaloper1...", * amount: "1000000", * publicKey: "base64EncodedPublicKey" * }; * const result = await delegateTool.invoke(input); * ``` */ export class DelegateTool extends StructuredTool { name = "delegate"; description = `Delegate tokens to a validator on Oraichain. Inputs: delegatorAddress: string - The address of the delegator. validatorAddress: string - The address of the validator to delegate to. amount: string - The amount to delegate to. If the denom is fully uppercase, multiply the input amount by 10^6. If the denom is fully lowercase, let it be. denom: string - The denom of the amount to delegate to. publicKey: string - The delegator's public key for signing. `; schema = z.object({ delegatorAddress: z.string().describe("The address of the delegator"), validatorAddress: z .string() .describe("The address of the validator to delegate to"), amount: z .string() .describe( "The amount to delegate to. If the denom is fully uppercase, multiply the input amount by 10^6. If the denom is fully lowercase, let it be." ), denom: z.string().describe("The denom of the amount to delegate to."), publicKey: z.string().describe("The delegator's public key for signing"), }); constructor(private readonly oraichainKit: OraichainAgentKit) { super(); } protected async _call(input: z.infer<typeof this.schema>): Promise<string> { try { const amount: Coin = { denom: input.denom, amount: input.amount, }; const signDoc = await this.oraichainKit.buildSignDoc( input.delegatorAddress, input.publicKey, [ { typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", value: { delegatorAddress: input.delegatorAddress, validatorAddress: input.validatorAddress, amount, }, }, ], "auto", "" ); return JSON.stringify({ status: "success", data: { signDoc: Buffer.from(makeSignBytes(signDoc)).toString("base64"), }, }); } catch (error: any) { return JSON.stringify({ status: "error", message: error.message, code: error.code || "UNKNOWN_ERROR", }); } } } if (import.meta.vitest) { const { describe, it, expect, vi } = import.meta.vitest; vi.mock("@cosmjs/proto-signing", () => ({ makeSignBytes: vi.fn().mockReturnValue(new Uint8Array([1, 2, 3])), })); describe("DelegateTool", () => { const mockBuildSignDoc = vi.fn(); const mockOraichainKit = { buildSignDoc: mockBuildSignDoc, } as unknown as OraichainAgentKit; const tool = new DelegateTool(mockOraichainKit); it("should successfully create a delegation transaction", async () => { const input = { delegatorAddress: "orai1g4h64ksa6jqgae22h8pxfcaksg93ldj48jy2r7", validatorAddress: "oraivaloper1s395lfdpv9l5y38nwr07xs0jq5jhhqs7mjt68c", amount: "1000000.0", // Added decimal for clarity denom: "orai", // Added required denom field publicKey: "AytM0l1GZg4Z5YCLoWjmqD+5qKzM5tN8EzrKtlGmH7g=", // Sample base64 pubkey }; const mockSignDoc = { /* mock sign doc structure */ }; mockBuildSignDoc.mockResolvedValueOnce(mockSignDoc); const result = await tool.invoke(input); const parsedResult = JSON.parse(result); expect(parsedResult.status).toBe("success"); expect(parsedResult.data.signDoc).toBe("AQID"); // base64 of [1,2,3] expect(mockBuildSignDoc).toHaveBeenCalledWith( input.delegatorAddress, input.publicKey, [ { typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", value: { delegatorAddress: input.delegatorAddress, validatorAddress: input.validatorAddress, amount: { denom: input.denom, amount: input.amount, }, }, }, ], "auto", "" ); }); it("should handle invalid input", async () => { const input = { // Missing required fields delegatorAddress: "orai1...", }; try { await tool.invoke(input); } catch (error) { expect(error.message).toContain( "Received tool input did not match expected schema" ); } }); it("should handle buildSignDoc errors", async () => { const input = { delegatorAddress: "orai1...", validatorAddress: "oraivaloper1...", amount: "1000000", denom: "orai", // Added required denom field publicKey: "base64PublicKey", }; const errorMessage = "Failed to build sign doc"; mockBuildSignDoc.mockRejectedValueOnce(new Error(errorMessage)); const result = await tool.invoke(input); const parsedResult = JSON.parse(result); expect(parsedResult.status).toBe("error"); expect(parsedResult.message).toBe(errorMessage); }); }); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/oraichain/orai-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server