diff --git a/packages/plugin-hedera/src/actions/claim-airdrop/claim-airdrop.ts b/packages/plugin-hedera/src/actions/claim-airdrop/claim-airdrop.ts new file mode 100644 index 00000000000..aaa863cc74f --- /dev/null +++ b/packages/plugin-hedera/src/actions/claim-airdrop/claim-airdrop.ts @@ -0,0 +1,117 @@ +import { + Action, + composeContext, + elizaLogger, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, +} from "@elizaos/core"; +import { HederaProvider } from "../../providers/client"; +import { claimAirdropTemplate } from "../../templates"; +import { claimAirdropParamsSchema } from "./schema.ts"; +import { ClaimAirdropService } from "./services/claim-airdrop-service.ts"; + +export const claimAirdropAction: Action = { + name: "HEDERA_CLAIM_AIRDROP", + description: "Claim available pending token airdrop", + handler: async ( + runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: { [key: string]: unknown }, + _callback?: HandlerCallback + ) => { + console.log("invoke"); + + const claimAirdropContext = composeContext({ + state: state, + template: claimAirdropTemplate, + templatingEngine: "handlebars", + }); + + console.log("test"); + + const claimAirdropContent = await generateObjectDeprecated({ + runtime: runtime, + context: claimAirdropContext, + modelClass: ModelClass.SMALL, + }); + + try { + console.log(claimAirdropContent); + + const claimAirdropData = + claimAirdropParamsSchema.parse(claimAirdropContent); + + console.log(claimAirdropData); + + const accountId = runtime.getSetting("HEDERA_ACCOUNT_ID"); + + const hederaProvider = new HederaProvider(runtime); + const action = new ClaimAirdropService(hederaProvider); + + await action.execute(claimAirdropData, accountId); + + await _callback({ + text: `Successfully claimed airdrop for token ${claimAirdropData.tokenId}`, + }); + + return true; + } catch (error) { + elizaLogger.error("Error during claiming airdrop:", error); + + if (_callback) { + await _callback({ + text: `Error during claiming airdrop: ${error.message}`, + content: { error: error.message }, + }); + } + return false; + } + }, + validate: async (runtime: IAgentRuntime) => { + const privateKey = runtime.getSetting("HEDERA_PRIVATE_KEY"); + const accountAddress = runtime.getSetting("HEDERA_ACCOUNT_ID"); + const selectedNetworkType = runtime.getSetting("HEDERA_NETWORK_TYPE"); + + return !!(privateKey && accountAddress && selectedNetworkType); + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Claim airdrop (1) 5 Tokens ({{0.0.5445766}}) from {{0.0.5393076}}", + action: "HEDERA_CLAIM_AIRDROP", + }, + }, + { + user: "{{user2}}", + content: { + text: "", + action: "HEDERA_CLAIM_AIRDROP", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Claim airdrop (2) 50 Tokens ({{0.0.5447843}}) from {{0.0.5393076}}", + action: "HEDERA_CLAIM_AIRDROP", + }, + }, + { + user: "{{user2}}", + content: { + text: "", + action: "HEDERA_CLAIM_AIRDROP", + }, + }, + ], + ], + similes: ["CLAIM_AIRDROP", "CLAIM_TOKEN_AIRDROP", "CLAIM_TOKEN"], +}; diff --git a/packages/plugin-hedera/src/actions/claim-airdrop/schema.ts b/packages/plugin-hedera/src/actions/claim-airdrop/schema.ts new file mode 100644 index 00000000000..bbbbe6c8211 --- /dev/null +++ b/packages/plugin-hedera/src/actions/claim-airdrop/schema.ts @@ -0,0 +1,6 @@ +import { z } from "zod"; + +export const claimAirdropParamsSchema = z.object({ + senderId: z.string(), + tokenId: z.string(), +}); diff --git a/packages/plugin-hedera/src/actions/claim-airdrop/services/claim-airdrop-service.ts b/packages/plugin-hedera/src/actions/claim-airdrop/services/claim-airdrop-service.ts new file mode 100644 index 00000000000..3833f3b6c00 --- /dev/null +++ b/packages/plugin-hedera/src/actions/claim-airdrop/services/claim-airdrop-service.ts @@ -0,0 +1,35 @@ +import { HederaProvider } from "../../../providers/client"; +import { AccountId, PendingAirdropId, TokenId } from "@hashgraph/sdk"; +import { ClaimAirdropData } from "../types.ts"; + +export class ClaimAirdropService { + constructor(private hederaProvider: HederaProvider) {} + + async execute(params: ClaimAirdropData, accountId: string): Promise { + if (!params.tokenId) { + throw new Error("No tokenId provided"); + } + + if (!params.senderId) { + throw new Error("No senderId provided"); + } + + if (!accountId) { + throw new Error("No accountId provided"); + } + + const tokenId = TokenId.fromString(params.tokenId); + const senderId = AccountId.fromString(params.senderId); + const receiverId = AccountId.fromString(accountId); + + const pendingAirdrop = new PendingAirdropId({ + senderId, + tokenId, + receiverId, + }); + + const agentKit = this.hederaProvider.getHederaAgentKit(); + + return agentKit.claimAirdrop(pendingAirdrop); + } +} diff --git a/packages/plugin-hedera/src/actions/claim-airdrop/types.ts b/packages/plugin-hedera/src/actions/claim-airdrop/types.ts new file mode 100644 index 00000000000..c82fb914131 --- /dev/null +++ b/packages/plugin-hedera/src/actions/claim-airdrop/types.ts @@ -0,0 +1,4 @@ +import { claimAirdropParamsSchema } from "./schema.ts"; +import { z } from "zod"; + +export type ClaimAirdropData = z.infer; diff --git a/packages/plugin-hedera/src/index.ts b/packages/plugin-hedera/src/index.ts index 197a0a62805..01d08112836 100644 --- a/packages/plugin-hedera/src/index.ts +++ b/packages/plugin-hedera/src/index.ts @@ -10,6 +10,7 @@ import { tokenHoldersAction } from "./actions/token-holders/token-holders.ts"; import { airdropTokenAction } from "./actions/airdrop-token/airdrop-token.ts"; import { rejectTokenAction } from "./actions/reject-token/reject-token.ts"; import { pendingAirdropsAction } from "./actions/pending-airdrops/pending-airdrops.ts"; +import { claimAirdropAction } from "./actions/claim-airdrop/claim-airdrop.ts"; export const hederaPlugin: Plugin = { name: "Hedera", @@ -28,6 +29,7 @@ export const hederaPlugin: Plugin = { airdropTokenAction, rejectTokenAction, pendingAirdropsAction, + claimAirdropAction, ], }; diff --git a/packages/plugin-hedera/src/templates/index.ts b/packages/plugin-hedera/src/templates/index.ts index 1e270bb60b6..05304b188bf 100644 --- a/packages/plugin-hedera/src/templates/index.ts +++ b/packages/plugin-hedera/src/templates/index.ts @@ -164,3 +164,48 @@ Example response for the input: "Show me airdrops for 0.0.5422268", the response Now respond with a JSON markdown block containing only the extracted values. `; + +export const claimAirdropTemplate = `Given the recent messages and wallet information below: +{{recentMessages}} +{{walletInfo}} +Extract data of pending token airdrop from message with following instructions. +1. **Sender Id** + - Sender Id should looks like "0.0.5422268" and should be string. + - Sender Id as string cant have other chars than numbers 0 to 9 and dots. + - Dots cant start accountId string or end, there is always number on start and end. + - Example sender ids are "0.0.5422268", "0.0.4515756" + +3. **Token Id** + - Token Id should looks like "0.0.5422268" and should be string. + - Token Id as string cant have other chars than numbers 0 to 9 and dots. + - Dots cant start accountId string or end, there is always number on start and end. + - Example token ids are "0.0.5447843", "0.0.4515756" + +Respond with a JSON markdown block containing only the extracted values: +\`\`\`json +{ + "senderId": string, // The senderId for example "0.0.4515756" + "tokenId": string // The tokenId for example "0.0.4515756" +} +\`\`\` + +The message commonly have structure like "Claim airdrop (1) 5 Tokens (TOKEN_ID) from SENDER_ID" where TOKEN_ID and SENDER_ID are variables to extract. + +Example response for the input: "Claim airdrop (1) 5 Tokens (0.0.5445766) from 0.0.5393076", the response should be: +\`\`\`json +{ + "senderId": "0.0.5393076", + "tokenId": "0.0.5445766" +} +\`\`\` + +Example response for the input: "Claim airdrop (2) 50 Tokens (0.0.5447843) from 0.0.5393076", the response should be: +\`\`\`json +{ + "senderId": "0.0.5393076", + "tokenId": "0.0.5447843" +} +\`\`\` + +Now respond with a JSON markdown block containing only the extracted values. +`;