@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.
408 lines (407 loc) • 11.9 kB
JavaScript
import { TopicCreateTransaction, AccountId, TopicMessageSubmitTransaction, TopicId, Hbar } from "@hashgraph/sdk";
import { HCS20BaseClient } from "./standards-sdk.es18.js";
import { PointsValidationError, TopicRegistrationError } from "./standards-sdk.es17.js";
class BrowserHCS20Client extends HCS20BaseClient {
constructor(config) {
super({
network: config.network,
logger: config.logger,
mirrorNodeUrl: config.mirrorNodeUrl,
registryTopicId: config.registryTopicId,
publicTopicId: config.publicTopicId
});
this.hwc = config.hwc;
this.feeAmount = config.feeAmount || 20;
}
/**
* Get operator account ID
*/
getOperatorId() {
const accountInfo = this.hwc.getAccountInfo();
if (!accountInfo?.accountId) {
throw new Error("Wallet not connected");
}
return accountInfo.accountId;
}
/**
* Deploy new points
*/
async deployPoints(options) {
const operatorId = this.getOperatorId();
const { progressCallback } = options;
try {
progressCallback?.({
stage: "creating-topic",
percentage: 20
});
let topicId;
if (options.usePrivateTopic) {
const publicKey = await this.mirrorNode.getPublicKey(operatorId);
const topicCreateTx = new TopicCreateTransaction().setTopicMemo(options.topicMemo || `HCS-20: ${options.name}`).setSubmitKey(publicKey).setAdminKey(publicKey).setAutoRenewAccountId(AccountId.fromString(operatorId));
const txResponse = await this.hwc.executeTransactionWithErrorHandling(
topicCreateTx,
false
);
if (txResponse?.error) {
throw new Error(txResponse.error);
}
const receipt = txResponse?.result;
if (!receipt?.topicId) {
throw new Error("Failed to create topic: topicId is null");
}
topicId = receipt.topicId.toString();
this.logger.info(`Created private topic: ${topicId}`);
} else {
topicId = this.publicTopicId;
}
progressCallback?.({
stage: "submitting-deploy",
percentage: 50,
topicId
});
const deployMessage = {
p: "hcs-20",
op: "deploy",
name: options.name,
tick: this.normalizeTick(options.tick),
max: options.maxSupply,
lim: options.limitPerMint,
metadata: options.metadata,
m: options.topicMemo
};
const validation = this.validateMessage(deployMessage);
if (!validation.valid) {
throw new PointsValidationError(
"Invalid deploy message",
validation.errors
);
}
const deployResult = await this.submitPayload(
topicId,
deployMessage,
options.usePrivateTopic
);
const deployTxId = deployResult.transactionHash?.toString() || "";
progressCallback?.({
stage: "confirming",
percentage: 80,
topicId,
deployTxId
});
await new Promise((resolve) => setTimeout(resolve, 2e3));
progressCallback?.({
stage: "complete",
percentage: 100,
topicId,
deployTxId
});
return {
name: options.name,
tick: this.normalizeTick(options.tick),
maxSupply: options.maxSupply,
limitPerMint: options.limitPerMint,
metadata: options.metadata,
topicId,
deployerAccountId: operatorId,
currentSupply: "0",
deploymentTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
isPrivate: options.usePrivateTopic || false
};
} catch (error) {
progressCallback?.({
stage: "complete",
percentage: 100,
error: error instanceof Error ? error.message : "Unknown error"
});
throw error;
}
}
/**
* Mint points
*/
async mintPoints(options) {
this.getOperatorId();
const { progressCallback } = options;
try {
progressCallback?.({
stage: "validating",
percentage: 20
});
progressCallback?.({
stage: "submitting",
percentage: 50
});
const mintMessage = {
p: "hcs-20",
op: "mint",
tick: this.normalizeTick(options.tick),
amt: options.amount,
to: this.accountToString(options.to),
m: options.memo
};
const topicId = options.topicId || this.publicTopicId;
const mintResult = await this.submitPayload(
topicId,
mintMessage,
false
);
const mintTxId = mintResult.transactionHash?.toString() || "";
progressCallback?.({
stage: "confirming",
percentage: 80,
mintTxId
});
await new Promise((resolve) => setTimeout(resolve, 2e3));
progressCallback?.({
stage: "complete",
percentage: 100,
mintTxId
});
return {
id: mintTxId,
operation: "mint",
tick: this.normalizeTick(options.tick),
amount: options.amount,
to: this.accountToString(options.to),
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
sequenceNumber: 0,
topicId,
transactionId: mintTxId,
memo: options.memo
};
} catch (error) {
progressCallback?.({
stage: "complete",
percentage: 100,
error: error instanceof Error ? error.message : "Unknown error"
});
throw error;
}
}
/**
* Transfer points
*/
async transferPoints(options) {
this.getOperatorId();
const { progressCallback } = options;
try {
progressCallback?.({
stage: "validating-balance",
percentage: 20
});
progressCallback?.({
stage: "submitting",
percentage: 50
});
const transferMessage = {
p: "hcs-20",
op: "transfer",
tick: this.normalizeTick(options.tick),
amt: options.amount,
from: this.accountToString(options.from),
to: this.accountToString(options.to),
m: options.memo
};
const topicId = options.topicId || this.publicTopicId;
const transferResult = await this.submitPayload(
topicId,
transferMessage,
false
);
const transferTxId = transferResult.transactionHash?.toString() || "";
progressCallback?.({
stage: "confirming",
percentage: 80,
transferTxId
});
await new Promise((resolve) => setTimeout(resolve, 2e3));
progressCallback?.({
stage: "complete",
percentage: 100,
transferTxId
});
return {
id: transferTxId,
operation: "transfer",
tick: this.normalizeTick(options.tick),
amount: options.amount,
from: this.accountToString(options.from),
to: this.accountToString(options.to),
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
sequenceNumber: 0,
topicId,
transactionId: transferTxId,
memo: options.memo
};
} catch (error) {
progressCallback?.({
stage: "complete",
percentage: 100,
error: error instanceof Error ? error.message : "Unknown error"
});
throw error;
}
}
/**
* Burn points
*/
async burnPoints(options) {
this.getOperatorId();
const { progressCallback } = options;
try {
progressCallback?.({
stage: "validating-balance",
percentage: 20
});
progressCallback?.({
stage: "submitting",
percentage: 50
});
const burnMessage = {
p: "hcs-20",
op: "burn",
tick: this.normalizeTick(options.tick),
amt: options.amount,
from: this.accountToString(options.from),
m: options.memo
};
const topicId = options.topicId || this.publicTopicId;
const burnResult = await this.submitPayload(
topicId,
burnMessage,
false
);
const burnTxId = burnResult.transactionHash?.toString() || "";
progressCallback?.({
stage: "confirming",
percentage: 80,
burnTxId
});
await new Promise((resolve) => setTimeout(resolve, 2e3));
progressCallback?.({
stage: "complete",
percentage: 100,
burnTxId
});
return {
id: burnTxId,
operation: "burn",
tick: this.normalizeTick(options.tick),
amount: options.amount,
from: this.accountToString(options.from),
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
sequenceNumber: 0,
topicId,
transactionId: burnTxId,
memo: options.memo
};
} catch (error) {
progressCallback?.({
stage: "complete",
percentage: 100,
error: error instanceof Error ? error.message : "Unknown error"
});
throw error;
}
}
/**
* Register a topic in the registry
*/
async registerTopic(options) {
const { progressCallback } = options;
try {
progressCallback?.({
stage: "validating",
percentage: 20
});
const registerMessage = {
p: "hcs-20",
op: "register",
name: options.name,
metadata: options.metadata,
private: options.isPrivate,
t_id: this.topicToString(options.topicId),
m: options.memo
};
const validation = this.validateMessage(registerMessage);
if (!validation.valid) {
throw new PointsValidationError(
"Invalid register message",
validation.errors
);
}
progressCallback?.({
stage: "submitting",
percentage: 50
});
const registerResult = await this.submitPayload(
this.registryTopicId,
registerMessage,
false
);
const registerTxId = registerResult.transactionHash?.toString() || "";
progressCallback?.({
stage: "confirming",
percentage: 80,
registerTxId
});
await new Promise((resolve) => setTimeout(resolve, 2e3));
progressCallback?.({
stage: "complete",
percentage: 100,
registerTxId
});
} catch (error) {
progressCallback?.({
stage: "complete",
percentage: 100,
error: error instanceof Error ? error.message : "Unknown error"
});
throw new TopicRegistrationError(
error instanceof Error ? error.message : "Unknown error",
this.topicToString(options.topicId)
);
}
}
/**
* Submit payload to topic using HWC
*/
async submitPayload(topicId, payload, requiresFee) {
this.logger.debug(`Submitting payload to topic ${topicId}`);
let message;
if (typeof payload === "string") {
message = payload;
} else {
message = JSON.stringify(payload);
}
const transaction = new TopicMessageSubmitTransaction().setTopicId(TopicId.fromString(topicId)).setMessage(message);
if (requiresFee) {
this.logger.info(
"Topic requires fee payment, setting max transaction fee"
);
transaction.setMaxTransactionFee(new Hbar(this.feeAmount));
}
const transactionResponse = await this.hwc.executeTransactionWithErrorHandling(
transaction,
false
);
if (transactionResponse?.error) {
this.logger.error(
`Failed to submit payload: ${transactionResponse.error}`
);
throw new Error(`Failed to submit payload: ${transactionResponse.error}`);
}
if (!transactionResponse?.result) {
this.logger.error(
"Failed to submit message: receipt is null or undefined"
);
throw new Error("Failed to submit message: receipt is null or undefined");
}
this.logger.debug("Payload submitted successfully via HWC");
return transactionResponse.result;
}
}
export {
BrowserHCS20Client
};
//# sourceMappingURL=standards-sdk.es19.js.map