@hashgraphonline/standards-sdk
Version:
The Hashgraph Online Standards SDK provides a complete implementation of the Hashgraph Consensus Standards (HCS), giving developers all the tools needed to build applications on Hedera.
899 lines (898 loc) • 30.2 kB
JavaScript
import { Logger } from "./standards-sdk.es22.js";
import { Registration } from "./standards-sdk.es9.js";
import { HCS11Client } from "./standards-sdk.es14.js";
import { PublicKey } from "@hashgraph/sdk";
import { HederaMirrorNode } from "./standards-sdk.es29.js";
import { HRLResolver } from "./standards-sdk.es25.js";
var Hcs10MemoType = /* @__PURE__ */ ((Hcs10MemoType2) => {
Hcs10MemoType2["INBOUND"] = "inbound";
Hcs10MemoType2["OUTBOUND"] = "outbound";
Hcs10MemoType2["CONNECTION"] = "connection";
Hcs10MemoType2["REGISTRY"] = "registry";
return Hcs10MemoType2;
})(Hcs10MemoType || {});
class HCS10BaseClient extends Registration {
constructor(config) {
super();
this.network = config.network;
this.logger = Logger.getInstance({
level: config.logLevel || "info",
module: "HCS10-BaseClient",
prettyPrint: config.prettyPrint,
silent: config.silent
});
this.mirrorNode = new HederaMirrorNode(
config.network,
this.logger,
config.mirrorNode
);
this.feeAmount = config.feeAmount || 1e-3;
}
/**
* Updates the mirror node configuration.
* @param config The new mirror node configuration.
*/
configureMirrorNode(config) {
this.mirrorNode.configureMirrorNode(config);
this.logger.info("Mirror node configuration updated");
}
extractTopicFromOperatorId(operatorId) {
if (!operatorId) {
return "";
}
const parts = operatorId.split("@");
if (parts.length > 0) {
return parts[0];
}
return "";
}
extractAccountFromOperatorId(operatorId) {
if (!operatorId) {
return "";
}
const parts = operatorId.split("@");
if (parts.length > 1) {
return parts[1];
}
return "";
}
/**
* Get a stream of messages from a connection topic
* @param topicId The connection topic ID to get messages from
* @param options Optional filtering options for messages
* @returns A stream of filtered messages valid for connection topics
*/
async getMessageStream(topicId, options) {
try {
const messages = await this.mirrorNode.getTopicMessages(topicId, options);
const validOps = ["message", "close_connection", "transaction"];
const filteredMessages = messages.filter((msg) => {
if (msg.p !== "hcs-10" || !validOps.includes(msg.op)) {
return false;
}
if (msg.op === "message" || msg.op === "close_connection") {
if (!msg.operator_id) {
return false;
}
if (!this.isValidOperatorId(msg.operator_id)) {
return false;
}
if (msg.op === "message" && !msg.data) {
return false;
}
}
if (msg.op === "transaction") {
if (!msg.operator_id || !msg.schedule_id) {
return false;
}
if (!this.isValidOperatorId(msg.operator_id)) {
return false;
}
}
return true;
});
return {
messages: filteredMessages
};
} catch (error) {
if (this.logger) {
this.logger.error(`Error fetching messages: ${error.message}`);
}
return { messages: [] };
}
}
/**
* Public method to retrieve topic information using the internal mirror node client.
*
* @param topicId The ID of the topic to query.
* @returns Topic information or null if not found or an error occurs.
*/
async getPublicTopicInfo(topicId) {
try {
return await this.mirrorNode.getTopicInfo(topicId);
} catch (error) {
this.logger.error(
`Error getting public topic info for ${topicId}:`,
error
);
return null;
}
}
/**
* Checks if a user can submit to a topic and determines if a fee is required
* @param topicId The topic ID to check
* @param userAccountId The account ID of the user attempting to submit
* @returns Object with canSubmit, requiresFee, and optional reason
*/
async canSubmitToTopic(topicId, userAccountId) {
try {
const topicInfo = await this.mirrorNode.getTopicInfo(topicId);
if (!topicInfo) {
return {
canSubmit: false,
requiresFee: false,
reason: "Topic does not exist"
};
}
if (!topicInfo.submit_key?.key) {
return { canSubmit: true, requiresFee: false };
}
try {
const userPublicKey = await this.mirrorNode.getPublicKey(userAccountId);
if (topicInfo.submit_key._type === "ProtobufEncoded") {
const keyBytes = Buffer.from(topicInfo.submit_key.key, "hex");
const hasAccess = await this.mirrorNode.checkKeyListAccess(
keyBytes,
userPublicKey
);
if (hasAccess) {
return { canSubmit: true, requiresFee: false };
}
} else {
const topicSubmitKey = PublicKey.fromString(topicInfo.submit_key.key);
if (userPublicKey.toString() === topicSubmitKey.toString()) {
return { canSubmit: true, requiresFee: false };
}
}
} catch (error) {
this.logger.error(
`Key validation error: ${error instanceof Error ? error.message : String(error)}`
);
}
if (topicInfo.fee_schedule_key?.key && topicInfo.custom_fees?.fixed_fees?.length > 0) {
return {
canSubmit: true,
requiresFee: true,
reason: "Requires fee payment via HIP-991"
};
}
return {
canSubmit: false,
requiresFee: false,
reason: "User does not have submit permission for this topic"
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error(`Topic submission validation error: ${errorMessage}`);
return {
canSubmit: false,
requiresFee: false,
reason: `Error: ${errorMessage}`
};
}
}
/**
* Get all messages from a topic
* @param topicId The topic ID to get messages from
* @param options Optional filtering options for messages
* @returns All messages from the topic
*/
async getMessages(topicId, options) {
try {
const messages = await this.mirrorNode.getTopicMessages(topicId, options);
const validatedMessages = messages.filter((msg) => {
if (msg.p !== "hcs-10") {
return false;
}
if (msg.op === "message") {
if (!msg.data) {
return false;
}
if (msg.operator_id) {
if (!this.isValidOperatorId(msg.operator_id)) {
return false;
}
}
}
return true;
});
return {
messages: validatedMessages
};
} catch (error) {
if (this.logger) {
this.logger.error(`Error fetching messages: ${error.message}`);
}
return { messages: [] };
}
}
/**
* Requests an account from the mirror node
* @param account The account ID to request
* @returns The account response
*/
async requestAccount(account) {
try {
if (!account) {
throw new Error("Account ID is required");
}
return await this.mirrorNode.requestAccount(account);
} catch (e) {
this.logger.error("Failed to fetch account", e);
throw e;
}
}
/**
* Retrieves the memo for an account
* @param accountId The account ID to retrieve the memo for
* @returns The memo
*/
async getAccountMemo(accountId) {
return await this.mirrorNode.getAccountMemo(accountId);
}
/**
* Retrieves the profile for an account
* @param accountId The account ID to retrieve the profile for
* @param disableCache Whether to disable caching of the result
* @returns The profile
*/
async retrieveProfile(accountId, disableCache) {
this.logger.debug(`Retrieving profile for account: ${accountId}`);
const cacheKey = `${accountId}-${this.network}`;
if (!disableCache) {
const cachedProfileResponse = HCS10Cache.getInstance().get(cacheKey);
if (cachedProfileResponse) {
this.logger.debug(`Cache hit for profile: ${accountId}`);
return cachedProfileResponse;
}
}
try {
const hcs11Client = new HCS11Client({
network: this.network,
auth: {
operatorId: "0.0.0"
},
logLevel: "info"
});
const profileResult = await hcs11Client.fetchProfileByAccountId(
accountId,
this.network
);
if (!profileResult?.success) {
this.logger.error(
`Failed to retrieve profile for account ID: ${accountId}`,
profileResult?.error
);
return {
profile: null,
success: false,
error: profileResult?.error || `Failed to retrieve profile for account ID: ${accountId}`
};
}
const profile = profileResult.profile;
let topicInfo = null;
if (profileResult.topicInfo?.inboundTopic && profileResult.topicInfo?.outboundTopic && profileResult.topicInfo?.profileTopicId) {
topicInfo = {
inboundTopic: profileResult.topicInfo.inboundTopic,
outboundTopic: profileResult.topicInfo.outboundTopic,
profileTopicId: profileResult.topicInfo.profileTopicId
};
}
const responseToCache = {
profile,
topicInfo,
success: true
};
HCS10Cache.getInstance().set(cacheKey, responseToCache);
return responseToCache;
} catch (e) {
const error = e;
const logMessage = `Failed to retrieve profile: ${error.message}`;
this.logger.error(logMessage);
return {
profile: null,
success: false,
error: logMessage
};
}
}
/**
* @deprecated Use retrieveCommunicationTopics instead
* @param accountId The account ID to retrieve the outbound connect topic for
* @returns {TopicInfo} Topic Info from target profile.
*/
async retrieveOutboundConnectTopic(accountId) {
return await this.retrieveCommunicationTopics(accountId, true);
}
/**
* Retrieves the communication topics for an account
* @param accountId The account ID to retrieve the communication topics for
* @param disableCache Whether to disable caching of the result
* @returns {TopicInfo} Topic Info from target profile.
*/
async retrieveCommunicationTopics(accountId, disableCache) {
try {
const profileResponse = await this.retrieveProfile(
accountId,
disableCache
);
if (!profileResponse?.success) {
throw new Error(profileResponse.error || "Failed to retrieve profile");
}
const profile = profileResponse.profile;
if (!profile.inboundTopicId || !profile.outboundTopicId) {
throw new Error(
`Invalid HCS-11 profile for HCS-10 agent: missing inboundTopicId or outboundTopicId`
);
}
if (!profileResponse.topicInfo) {
throw new Error(
`TopicInfo is missing in the profile for account ${accountId}`
);
}
return profileResponse.topicInfo;
} catch (e) {
const error = e;
const logMessage = `Failed to retrieve topic info: ${error.message}`;
this.logger.error(logMessage);
throw error;
}
}
/**
* Retrieves outbound messages for an agent
* @param agentAccountId The account ID of the agent
* @param options Optional filtering options for messages
* @returns The outbound messages
*/
async retrieveOutboundMessages(agentAccountId, options) {
try {
const topicInfo = await this.retrieveCommunicationTopics(agentAccountId);
if (!topicInfo) {
this.logger.warn(
`No outbound connect topic found for agentAccountId: ${agentAccountId}`
);
return [];
}
const response = await this.getMessages(topicInfo.outboundTopic, options);
return response.messages.filter(
(msg) => msg.p === "hcs-10" && (msg.op === "connection_request" || msg.op === "connection_created" || msg.op === "message")
);
} catch (e) {
const error = e;
const logMessage = `Failed to retrieve outbound messages: ${error.message}`;
this.logger.error(logMessage);
return [];
}
}
/**
* Checks if a connection has been created for an agent
* @param agentAccountId The account ID of the agent
* @param connectionId The ID of the connection
* @returns True if the connection has been created, false otherwise
*/
async hasConnectionCreated(agentAccountId, connectionId) {
try {
const outBoundTopic = await this.retrieveCommunicationTopics(agentAccountId);
const messages = await this.retrieveOutboundMessages(
outBoundTopic.outboundTopic
);
return messages.some(
(msg) => msg.op === "connection_created" && msg.connection_id === connectionId
);
} catch (e) {
const error = e;
const logMessage = `Failed to check connection created: ${error.message}`;
this.logger.error(logMessage);
return false;
}
}
/**
* Gets message content, resolving any HRL references if needed
* @param data The data string that may contain an HRL reference
* @param forceRaw Whether to force returning raw binary data
* @returns The resolved content
*/
async getMessageContent(data, forceRaw = false) {
if (!data.match(/^hcs:\/\/(\d+)\/([0-9]+\.[0-9]+\.[0-9]+)$/)) {
return data;
}
try {
const resolver = new HRLResolver(this.logger.getLevel());
if (!resolver.isValidHRL(data)) {
return data;
}
const result = await resolver.resolveHRL(data, {
network: this.network,
returnRaw: forceRaw
});
return result.content;
} catch (e) {
const error = e;
const logMessage = `Error resolving HRL reference: ${error.message}`;
this.logger.error(logMessage);
throw new Error(logMessage);
}
}
/**
* Gets message content with its content type, resolving any HRL references if needed
* @param data The data string that may contain an HRL reference
* @param forceRaw Whether to force returning raw binary data
* @returns The resolved content along with content type information
*/
async getMessageContentWithType(data, forceRaw = false) {
if (!data.match(/^hcs:\/\/(\d+)\/([0-9]+\.[0-9]+\.[0-9]+)$/)) {
return {
content: data,
contentType: "text/plain",
isBinary: false
};
}
try {
const resolver = new HRLResolver(this.logger.getLevel());
return await resolver.getContentWithType(data, {
network: this.network,
returnRaw: forceRaw
});
} catch (e) {
const error = e;
const logMessage = `Error resolving HRL reference with type: ${error.message}`;
this.logger.error(logMessage);
throw new Error(logMessage);
}
}
/**
* Submits a connection request to an inbound topic
* @param inboundTopicId The ID of the inbound topic
* @param memo An optional memo for the message
* @returns The transaction receipt
*/
async submitConnectionRequest(inboundTopicId, memo) {
const accountResponse = this.getAccountAndSigner();
if (!accountResponse?.accountId) {
throw new Error("Operator account ID is not set");
}
const operatorId = await this.getOperatorId();
const accountId = accountResponse.accountId;
const submissionCheck = await this.canSubmitToTopic(
inboundTopicId,
accountId
);
if (!submissionCheck?.canSubmit) {
throw new Error(`Cannot submit to topic: ${submissionCheck.reason}`);
}
const inboundAccountOwner = await this.retrieveInboundAccountId(inboundTopicId);
if (!inboundAccountOwner) {
throw new Error("Failed to retrieve topic info account ID");
}
const connectionRequestMessage = {
p: "hcs-10",
op: "connection_request",
operator_id: operatorId,
m: memo
};
const requiresFee = submissionCheck.requiresFee;
const response = await this.submitPayload(
inboundTopicId,
connectionRequestMessage,
void 0,
requiresFee
);
this.logger.info(
`Submitted connection request to topic ID: ${inboundTopicId}`
);
const outboundTopic = await this.retrieveCommunicationTopics(accountId);
if (!outboundTopic) {
throw new Error("Failed to retrieve outbound topic");
}
const responseSequenceNumber = response.topicSequenceNumber?.toNumber();
if (!responseSequenceNumber) {
throw new Error("Failed to get response sequence number");
}
const requestorOperatorId = `${inboundTopicId}@${inboundAccountOwner}`;
await this.submitPayload(outboundTopic.outboundTopic, {
...connectionRequestMessage,
outbound_topic_id: outboundTopic.outboundTopic,
connection_request_id: responseSequenceNumber,
operator_id: requestorOperatorId
});
return response;
}
/**
* Records an outbound connection confirmation
* @param outboundTopicId The ID of the outbound topic
* @param connectionRequestId The ID of the connection request
* @param confirmedRequestId The ID of the confirmed request
* @param connectionTopicId The ID of the connection topic
* @param operatorId The operator ID of the original message sender.
* @param memo An optional memo for the message
*/
async recordOutboundConnectionConfirmation({
outboundTopicId,
requestorOutboundTopicId,
connectionRequestId,
confirmedRequestId,
connectionTopicId,
operatorId,
memo
}) {
const payload = {
p: "hcs-10",
op: "connection_created",
connection_topic_id: connectionTopicId,
outbound_topic_id: outboundTopicId,
requestor_outbound_topic_id: requestorOutboundTopicId,
confirmed_request_id: confirmedRequestId,
connection_request_id: connectionRequestId,
operator_id: operatorId,
m: memo
};
return await this.submitPayload(outboundTopicId, payload);
}
/**
* Waits for confirmation of a connection request
* @param inboundTopicId Inbound topic ID
* @param connectionRequestId Connection request ID
* @param maxAttempts Maximum number of attempts
* @param delayMs Delay between attempts in milliseconds
* @returns Connection confirmation details
*/
async waitForConnectionConfirmation(inboundTopicId, connectionRequestId, maxAttempts = 60, delayMs = 2e3, recordConfirmation = true) {
this.logger.info(
`Waiting for connection confirmation on inbound topic ${inboundTopicId} for request ID ${connectionRequestId}`
);
for (let attempt = 0; attempt < maxAttempts; attempt++) {
this.logger.info(
`Attempt ${attempt + 1}/${maxAttempts} to find connection confirmation`
);
const messages = await this.mirrorNode.getTopicMessages(inboundTopicId, {
order: "desc",
limit: 100
});
const connectionCreatedMessages = messages.filter(
(m) => m.op === "connection_created"
);
this.logger.info(
`Found ${connectionCreatedMessages.length} connection_created messages`
);
if (connectionCreatedMessages.length > 0) {
for (const message of connectionCreatedMessages) {
if (Number(message.connection_id) === Number(connectionRequestId)) {
const confirmationResult = {
connectionTopicId: message.connection_topic_id,
sequence_number: Number(message.sequence_number),
confirmedBy: message.operator_id,
memo: message.m
};
const confirmedByAccountId = this.extractAccountFromOperatorId(
confirmationResult.confirmedBy
);
const account = this.getAccountAndSigner();
const confirmedByConnectionTopics = await this.retrieveCommunicationTopics(confirmedByAccountId);
const agentConnectionTopics = await this.retrieveCommunicationTopics(account.accountId);
this.logger.info(
"Connection confirmation found",
confirmationResult
);
if (recordConfirmation) {
await this.recordOutboundConnectionConfirmation({
requestorOutboundTopicId: confirmedByConnectionTopics.outboundTopic,
outboundTopicId: agentConnectionTopics.outboundTopic,
connectionRequestId,
confirmedRequestId: confirmationResult.sequence_number,
connectionTopicId: confirmationResult.connectionTopicId,
operatorId: confirmationResult.confirmedBy,
memo: confirmationResult.memo || "Connection confirmed"
});
}
return confirmationResult;
}
}
}
if (attempt < maxAttempts - 1) {
this.logger.info(
`No matching confirmation found, waiting ${delayMs}ms before retrying...`
);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
throw new Error(
`Connection confirmation not found after ${maxAttempts} attempts for request ID ${connectionRequestId}`
);
}
/**
* Retrieves the operator ID for the current agent
* @param disableCache Whether to disable caching of the result
* @returns The operator ID
*/
async getOperatorId(disableCache) {
if (this.operatorId && !disableCache) {
return this.operatorId;
}
const accountResponse = this.getAccountAndSigner();
if (!accountResponse.accountId) {
throw new Error("Operator ID not found");
}
const profile = await this.retrieveProfile(accountResponse.accountId);
if (!profile.success) {
throw new Error("Failed to retrieve profile");
}
const operatorId = `${profile.topicInfo?.inboundTopic}@${accountResponse.accountId}`;
this.operatorId = operatorId;
return operatorId;
}
/**
* Retrieves the account ID of the owner of an inbound topic
* @param inboundTopicId The ID of the inbound topic
* @returns The account ID of the owner of the inbound topic
*/
async retrieveInboundAccountId(inboundTopicId) {
const topicInfo = await this.mirrorNode.getTopicInfo(inboundTopicId);
if (!topicInfo?.memo) {
throw new Error("Failed to retrieve topic info");
}
const topicInfoMemo = topicInfo.memo.toString();
const topicInfoParts = topicInfoMemo.split(":");
const inboundAccountOwner = topicInfoParts?.[4];
if (!inboundAccountOwner) {
throw new Error("Failed to retrieve topic info account ID");
}
return inboundAccountOwner;
}
clearCache() {
HCS10Cache.getInstance().clear();
}
/**
* Generates a standard HCS-10 memo string.
* @param type The type of topic memo ('inbound', 'outbound', 'connection').
* @param options Configuration options for the memo.
* @returns The formatted memo string.
* @protected
*/
_generateHcs10Memo(type, options) {
const ttl = options.ttl ?? 60;
switch (type) {
case "inbound":
if (!options.accountId) {
throw new Error("accountId is required for inbound memo");
}
return `hcs-10:0:${ttl}:0:${options.accountId}`;
case "outbound":
return `hcs-10:0:${ttl}:1`;
case "connection":
if (!options.inboundTopicId || options.connectionId === void 0) {
throw new Error(
"inboundTopicId and connectionId are required for connection memo"
);
}
return `hcs-10:1:${ttl}:2:${options.inboundTopicId}:${options.connectionId}`;
default:
throw new Error(`Invalid HCS-10 memo type: ${type}`);
}
}
/**
* Reads a topic's memo and determines its HCS-10 type
* @param topicId The topic ID to check
* @returns The HCS-10 memo type or null if not an HCS-10 topic
*/
async getTopicMemoType(topicId) {
try {
const topicInfo = await this.mirrorNode.getTopicInfo(topicId);
if (!topicInfo?.memo) {
this.logger.debug(`No memo found for topic ${topicId}`);
return null;
}
const memo = topicInfo.memo.toString();
if (!memo.startsWith("hcs-10:")) {
this.logger.debug(`Topic ${topicId} is not an HCS-10 topic`);
return null;
}
const parts = memo.split(":");
if (parts.length < 4) {
this.logger.warn(`Invalid HCS-10 memo format for topic ${topicId}: ${memo}`);
return null;
}
const typeEnum = parts[3];
switch (typeEnum) {
case "0":
return "inbound";
case "1":
return "outbound";
case "2":
return "connection";
case "3":
return "registry";
default:
this.logger.warn(`Unknown HCS-10 type enum: ${typeEnum} for topic ${topicId}`);
return null;
}
} catch (error) {
this.logger.error(`Error getting topic memo type for ${topicId}:`, error);
return null;
}
}
async checkRegistrationStatus(transactionId, network, baseUrl) {
try {
const response = await fetch(`${baseUrl}/api/request-confirm`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Network": network
},
body: JSON.stringify({ transaction_id: transactionId })
});
if (!response.ok) {
throw new Error(
`Failed to confirm registration: ${response.statusText}`
);
}
return await response.json();
} catch (e) {
const error = e;
const logMessage = `Error checking registration status: ${error.message}`;
this.logger.error(logMessage);
throw error;
}
}
/**
* Validates if an operator_id follows the correct format (agentTopicId@accountId)
* @param operatorId The operator ID to validate
* @returns True if the format is valid, false otherwise
*/
isValidOperatorId(operatorId) {
if (!operatorId) {
return false;
}
const parts = operatorId.split("@");
if (parts.length !== 2) {
return false;
}
const agentTopicId = parts[0];
const accountId = parts[1];
if (!agentTopicId) {
return false;
}
if (!accountId) {
return false;
}
const hederaIdPattern = /^[0-9]+\.[0-9]+\.[0-9]+$/;
if (!hederaIdPattern.test(accountId)) {
return false;
}
if (!hederaIdPattern.test(agentTopicId)) {
return false;
}
return true;
}
/**
* Retrieves all transaction requests from a topic
* @param topicId The topic ID to retrieve transactions from
* @param options Optional filtering and retrieval options
* @returns Array of transaction requests sorted by timestamp (newest first)
*/
async getTransactionRequests(topicId, options) {
this.logger.debug(`Retrieving transaction requests from topic ${topicId}`);
const { messages } = await this.getMessageStream(topicId, {
limit: options?.limit,
sequenceNumber: options?.sequenceNumber,
order: options?.order || "desc"
});
const transactOperations = messages.filter((m) => m.op === "transaction" && m.schedule_id).map((m) => ({
operator_id: m.operator_id || "",
schedule_id: m.schedule_id || "",
data: m.data || "",
memo: m.m,
sequence_number: Number(m.sequence_number)
})).sort((a, b) => {
if (a.sequence_number && b.sequence_number) {
return b.sequence_number - a.sequence_number;
}
return 0;
});
const result = options?.limit ? transactOperations.slice(0, options.limit) : transactOperations;
return result;
}
/**
* Gets the HCS-10 transaction memo for analytics based on the operation type
* @param payload The operation payload
* @returns The transaction memo in format hcs-10:op:{operation_enum}:{topic_type_enum}
*/
getHcs10TransactionMemo(payload) {
if (typeof payload !== "object" || !("op" in payload)) {
return null;
}
const typedPayload = payload;
const operation = typedPayload.op;
let operationEnum;
let topicTypeEnum;
switch (operation) {
case "register":
operationEnum = "0";
topicTypeEnum = "0";
break;
case "delete":
operationEnum = "1";
topicTypeEnum = "0";
break;
case "migrate":
operationEnum = "2";
topicTypeEnum = "0";
break;
case "connection_request":
operationEnum = "3";
topicTypeEnum = typedPayload.outbound_topic_id ? "2" : "1";
break;
case "connection_created":
operationEnum = "4";
topicTypeEnum = typedPayload.outbound_topic_id ? "2" : "1";
break;
case "connection_closed":
operationEnum = "5";
topicTypeEnum = typedPayload.outbound_topic_id ? "2" : "3";
break;
case "message":
operationEnum = "6";
topicTypeEnum = "3";
break;
case "close_connection":
operationEnum = "5";
topicTypeEnum = "3";
break;
case "transaction":
operationEnum = "6";
topicTypeEnum = "3";
break;
default:
operationEnum = "6";
topicTypeEnum = "3";
}
return `hcs-10:op:${operationEnum}:${topicTypeEnum}`;
}
}
class HCS10Cache {
constructor() {
this.CACHE_TTL = 36e5;
this.cache = /* @__PURE__ */ new Map();
this.cacheExpiry = /* @__PURE__ */ new Map();
}
static getInstance() {
if (!HCS10Cache.instance) {
HCS10Cache.instance = new HCS10Cache();
}
return HCS10Cache.instance;
}
set(key, value) {
this.cache.set(key, value);
this.cacheExpiry.set(key, Date.now() + this.CACHE_TTL);
}
get(key) {
const expiry = this.cacheExpiry.get(key);
if (expiry && expiry > Date.now()) {
return this.cache.get(key);
}
if (expiry) {
this.cache.delete(key);
this.cacheExpiry.delete(key);
}
return void 0;
}
clear() {
this.cache.clear();
this.cacheExpiry.clear();
}
}
export {
HCS10BaseClient,
HCS10Cache,
Hcs10MemoType
};
//# sourceMappingURL=standards-sdk.es5.js.map