@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera
107 lines (106 loc) • 4.33 kB
JavaScript
import { StructuredTool } from "@langchain/core/tools";
import { z } from "zod";
import { Logger } from "@hashgraphonline/standards-sdk";
class AirdropToolWrapper extends StructuredTool {
constructor(originalTool, agentKit) {
super();
this.name = "hedera-hts-airdrop-token";
this.description = "Airdrops fungible tokens to multiple recipients. Automatically converts human-readable amounts to smallest units based on token decimals.";
this.schema = z.object({
tokenId: z.string().describe('The ID of the fungible token to airdrop (e.g., "0.0.yyyy").'),
recipients: z.array(
z.object({
accountId: z.string().describe('Recipient account ID (e.g., "0.0.xxxx").'),
amount: z.union([z.number(), z.string()]).describe(
'Amount in human-readable format (e.g., "10" for 10 tokens).'
)
})
).min(1).describe("Array of recipient objects, each with accountId and amount."),
memo: z.string().optional().describe("Optional. Memo for the transaction.")
});
this.originalTool = originalTool;
this.agentKit = agentKit;
this.logger = new Logger({ module: "AirdropToolWrapper" });
}
async _call(input) {
try {
this.logger.info(
`Processing airdrop request for token ${input.tokenId} with ${input.recipients.length} recipients`
);
const tokenInfo = await this.getTokenInfo(input.tokenId);
const decimals = tokenInfo.decimals || 0;
this.logger.info(`Token ${input.tokenId} has ${decimals} decimal places`);
const convertedRecipients = input.recipients.map((recipient) => {
const humanAmount = typeof recipient.amount === "string" ? parseFloat(recipient.amount) : recipient.amount;
const smallestUnitAmount = this.convertToSmallestUnits(
humanAmount,
decimals
);
this.logger.info(
`Converting amount for ${recipient.accountId}: ${humanAmount} tokens → ${smallestUnitAmount} smallest units`
);
return {
...recipient,
amount: smallestUnitAmount.toString()
};
});
const convertedInput = {
...input,
recipients: convertedRecipients
};
this.logger.info(`Calling original airdrop tool with converted amounts`);
return await this.originalTool._call(convertedInput);
} catch (error) {
this.logger.error("Error in airdrop tool wrapper:", error);
throw error;
}
}
convertToSmallestUnits(amount, decimals) {
return Math.floor(amount * Math.pow(10, decimals));
}
async getTokenInfo(tokenId) {
return await this.queryTokenInfo(tokenId);
}
async queryTokenInfo(tokenId) {
try {
this.logger.info("Querying token info using mirror node");
const mirrorNode = this.agentKit.mirrorNode;
if (!mirrorNode) {
this.logger.info(
"MirrorNode not found in agentKit, attempting to access via fetch"
);
const network = this.agentKit.network || "testnet";
const mirrorNodeUrl = network === "mainnet" ? "https://mainnet.mirrornode.hedera.com" : "https://testnet.mirrornode.hedera.com";
const response = await fetch(
`${mirrorNodeUrl}/api/v1/tokens/${tokenId}`
);
if (response.ok) {
const tokenData = await response.json();
const decimals = parseInt(String(tokenData.decimals || "0"));
this.logger.info(
`Token ${tokenId} found with ${decimals} decimals via API`
);
return { ...tokenData, decimals };
}
} else {
const tokenData = await mirrorNode.getTokenInfo(tokenId);
if (tokenData && typeof tokenData.decimals !== "undefined") {
const decimals = parseInt(tokenData.decimals.toString()) || 0;
this.logger.info(`Token ${tokenId} found with ${decimals} decimals`);
return { ...tokenData, decimals };
}
}
throw new Error(`Token data not found or missing decimals field`);
} catch (error) {
this.logger.warn(`Failed to query token info for ${tokenId}:`, error);
this.logger.info(
"Falling back to assumed 0 decimal places (smallest units)"
);
return { decimals: 0 };
}
}
}
export {
AirdropToolWrapper
};
//# sourceMappingURL=index19.js.map