@hashgraphonline/hashinal-wc 1.0.117-canary.0 → 1.0.118-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,786 @@
1
+ import { Buffer } from "buffer";
2
+ import * as HashgraphSDK from "@hashgraph/sdk";
3
+ import { LedgerId, TopicMessageSubmitTransaction, TopicId, TransferTransaction, TransactionId, AccountId, Hbar, ContractExecuteTransaction, ContractId, TopicCreateTransaction, PrivateKey, TokenCreateTransaction, TokenType, TokenSupplyType, TokenMintTransaction, TokenId, AccountCreateTransaction, TokenAssociateTransaction, TokenDissociateTransaction, AccountUpdateTransaction, AccountAllowanceApproveTransaction } from "@hashgraph/sdk";
4
+ import { DAppConnector, HederaJsonRpcMethod, HederaSessionEvent, HederaChainId } from "@hashgraph/hedera-wallet-connect";
5
+ import { base64StringToSignatureMap, prefixMessageToSign, verifyMessageSignature } from "@hashgraph/hedera-wallet-connect";
6
+ import { Logger } from "./logger.js";
7
+ import { fetchWithRetry } from "./utils/retry.js";
8
+ import { Name, Result } from "./types.js";
9
+ function ensureGlobalHTMLElement() {
10
+ if (typeof globalThis === "undefined") {
11
+ return;
12
+ }
13
+ if (typeof globalThis.HTMLElement === "undefined") {
14
+ globalThis.HTMLElement = class {
15
+ };
16
+ }
17
+ }
18
+ ensureGlobalHTMLElement();
19
+ class HashinalsWalletConnectSDK {
20
+ constructor(logger, network) {
21
+ this.extensionCheckInterval = null;
22
+ this.hasCalledExtensionCallback = false;
23
+ this.logger = logger || new Logger();
24
+ this.network = network || LedgerId.MAINNET;
25
+ }
26
+ get dAppConnector() {
27
+ return HashinalsWalletConnectSDK.dAppConnectorInstance;
28
+ }
29
+ static getInstance(logger, network) {
30
+ let instance = HashinalsWalletConnectSDK?.instance;
31
+ if (!instance) {
32
+ HashinalsWalletConnectSDK.instance = new HashinalsWalletConnectSDK(
33
+ logger,
34
+ network
35
+ );
36
+ instance = HashinalsWalletConnectSDK.instance;
37
+ }
38
+ if (network) {
39
+ instance.setNetwork(network);
40
+ }
41
+ return instance;
42
+ }
43
+ setLogger(logger) {
44
+ this.logger = logger;
45
+ }
46
+ setNetwork(network) {
47
+ this.network = network;
48
+ }
49
+ getNetwork() {
50
+ return this.network;
51
+ }
52
+ setLogLevel(level) {
53
+ if (this.logger instanceof Logger) {
54
+ this.logger.setLogLevel(level);
55
+ }
56
+ }
57
+ async init(projectId, metadata, network, onSessionIframeCreated) {
58
+ const chosenNetwork = network || this.network;
59
+ const isMainnet = chosenNetwork.toString() === "mainnet";
60
+ if (HashinalsWalletConnectSDK.dAppConnectorInstance) {
61
+ return HashinalsWalletConnectSDK.dAppConnectorInstance;
62
+ }
63
+ HashinalsWalletConnectSDK.dAppConnectorInstance = new DAppConnector(
64
+ metadata,
65
+ chosenNetwork,
66
+ projectId,
67
+ Object.values(HederaJsonRpcMethod),
68
+ [HederaSessionEvent.ChainChanged, HederaSessionEvent.AccountsChanged],
69
+ [isMainnet ? HederaChainId.Mainnet : HederaChainId.Testnet],
70
+ "debug"
71
+ );
72
+ await HashinalsWalletConnectSDK.dAppConnectorInstance.init({
73
+ logger: "error"
74
+ });
75
+ HashinalsWalletConnectSDK.dAppConnectorInstance.onSessionIframeCreated = (session) => {
76
+ this.logger.info("new session from from iframe", session);
77
+ this.handleNewSession(session);
78
+ if (onSessionIframeCreated) {
79
+ onSessionIframeCreated(session);
80
+ }
81
+ };
82
+ this.logger.info(
83
+ `Hedera Wallet Connect SDK initialized on ${chosenNetwork}`
84
+ );
85
+ return HashinalsWalletConnectSDK.dAppConnectorInstance;
86
+ }
87
+ async connect() {
88
+ this.ensureInitialized();
89
+ const session = await this.dAppConnector.openModal();
90
+ this.handleNewSession(session);
91
+ return session;
92
+ }
93
+ async disconnect() {
94
+ try {
95
+ this.ensureInitialized();
96
+ const accountInfo = this.getAccountInfo();
97
+ const accountId = accountInfo?.accountId;
98
+ const network = accountInfo?.network;
99
+ const signer = this?.dAppConnector?.signers.find(
100
+ (signer_) => signer_.getAccountId().toString() === accountId
101
+ );
102
+ await this.dAppConnector?.disconnect(signer?.topic);
103
+ this.logger.info(`Disconnected from ${accountId} on ${network}`);
104
+ return true;
105
+ } catch (e) {
106
+ this.logger.error("Failed to disconnect", e);
107
+ return false;
108
+ }
109
+ }
110
+ async disconnectAll() {
111
+ try {
112
+ this.ensureInitialized();
113
+ await this.dAppConnector?.disconnectAll();
114
+ this.logger.info(`Disconnected from all wallets`);
115
+ return true;
116
+ } catch (e) {
117
+ this.logger.error("Failed to disconnect", e);
118
+ return false;
119
+ }
120
+ }
121
+ async executeTransaction(tx, disableSigner = false) {
122
+ this.ensureInitialized();
123
+ const accountInfo = this.getAccountInfo();
124
+ const accountId = accountInfo?.accountId;
125
+ const signer = this.dAppConnector.signers.find(
126
+ (signer_) => signer_.getAccountId().toString() === accountId
127
+ );
128
+ if (!disableSigner) {
129
+ const signedTx = await tx.freezeWithSigner(signer);
130
+ const executedTx = await signedTx.executeWithSigner(signer);
131
+ return await executedTx.getReceiptWithSigner(signer);
132
+ } else {
133
+ const executedTx = await tx.executeWithSigner(signer);
134
+ return await executedTx.getReceiptWithSigner(signer);
135
+ }
136
+ }
137
+ async executeTransactionWithErrorHandling(tx, disableSigner) {
138
+ try {
139
+ const result = await this.executeTransaction(tx, disableSigner);
140
+ return {
141
+ result,
142
+ error: void 0
143
+ };
144
+ } catch (e) {
145
+ const error = e;
146
+ const message = error.message?.toLowerCase();
147
+ this.logger.error("Failed to execute transaction", e);
148
+ this.logger.error("Failure reason for transaction is", message);
149
+ if (message.includes("insufficient payer balance")) {
150
+ return {
151
+ result: void 0,
152
+ error: "Insufficient balance to complete the transaction."
153
+ };
154
+ } else if (message.includes("reject")) {
155
+ return {
156
+ result: void 0,
157
+ error: "You rejected the transaction"
158
+ };
159
+ } else if (message.includes("invalid signature")) {
160
+ return {
161
+ result: void 0,
162
+ error: "Invalid signature. Please check your account and try again."
163
+ };
164
+ } else if (message.includes("transaction expired")) {
165
+ return {
166
+ result: void 0,
167
+ error: "Transaction expired. Please try again."
168
+ };
169
+ } else if (message.includes("account not found")) {
170
+ return {
171
+ result: void 0,
172
+ error: "Account not found. Please check the account ID and try again."
173
+ };
174
+ } else if (message.includes("unauthorized")) {
175
+ return {
176
+ result: void 0,
177
+ error: "Unauthorized. You may not have the necessary permissions for this action."
178
+ };
179
+ } else if (message.includes("busy")) {
180
+ return {
181
+ result: void 0,
182
+ error: "The network is busy. Please try again later."
183
+ };
184
+ } else if (message.includes("invalid transaction")) {
185
+ return {
186
+ result: void 0,
187
+ error: "Invalid transaction. Please check your inputs and try again."
188
+ };
189
+ }
190
+ }
191
+ }
192
+ async submitMessageToTopic(topicId, message, submitKey) {
193
+ this.ensureInitialized();
194
+ let transaction = new TopicMessageSubmitTransaction().setTopicId(TopicId.fromString(topicId)).setMessage(message);
195
+ if (submitKey) {
196
+ transaction = await transaction.sign(submitKey);
197
+ }
198
+ return this.executeTransaction(transaction);
199
+ }
200
+ async transferHbar(fromAccountId, toAccountId, amount) {
201
+ this.ensureInitialized();
202
+ const transaction = new TransferTransaction().setTransactionId(TransactionId.generate(fromAccountId)).addHbarTransfer(AccountId.fromString(fromAccountId), new Hbar(-amount)).addHbarTransfer(AccountId.fromString(toAccountId), new Hbar(amount));
203
+ return this.executeTransaction(transaction);
204
+ }
205
+ async executeSmartContract(contractId, functionName, parameters, gas = 1e5) {
206
+ this.ensureInitialized();
207
+ const transaction = new ContractExecuteTransaction().setContractId(ContractId.fromString(contractId)).setGas(gas).setFunction(functionName, parameters);
208
+ return this.executeTransaction(transaction);
209
+ }
210
+ handleNewSession(session) {
211
+ const sessionAccount = session.namespaces?.hedera?.accounts?.[0];
212
+ const sessionParts = sessionAccount?.split(":");
213
+ const accountId = sessionParts.pop();
214
+ const network = sessionParts.pop();
215
+ this.logger.info("sessionAccount is", accountId, network);
216
+ if (!accountId) {
217
+ this.logger.error("No account id found in the session");
218
+ return;
219
+ } else {
220
+ this.saveConnectionInfo(accountId, network);
221
+ }
222
+ }
223
+ getNetworkPrefix() {
224
+ const accountInfo = this.getAccountInfo();
225
+ const network = accountInfo?.network;
226
+ if (!network) {
227
+ this.logger.warn("Network is not set on SDK, defaulting.");
228
+ const cachedNetwork = localStorage.getItem("connectedNetwork");
229
+ if (cachedNetwork) {
230
+ return cachedNetwork;
231
+ }
232
+ return "mainnet-public";
233
+ }
234
+ if (network !== this.network) {
235
+ this.logger.warn(
236
+ "Detected network mismatch, reverting to signer network",
237
+ network
238
+ );
239
+ this.network = network;
240
+ }
241
+ return network.isMainnet() ? "mainnet-public" : "testnet";
242
+ }
243
+ async requestAccount(account) {
244
+ try {
245
+ const networkPrefix = this.getNetworkPrefix();
246
+ const url = `https://${networkPrefix}.mirrornode.hedera.com/api/v1/accounts/${account}`;
247
+ const response = await fetchWithRetry()(url);
248
+ if (!response.ok) {
249
+ throw new Error(
250
+ `Failed to make request to mirror node for account: ${response.status}`
251
+ );
252
+ }
253
+ return await response.json();
254
+ } catch (e) {
255
+ this.logger.error("Failed to fetch account", e);
256
+ throw e;
257
+ }
258
+ }
259
+ async getAccountBalance() {
260
+ this.ensureInitialized();
261
+ const accountInfo = this.getAccountInfo();
262
+ const account = accountInfo?.accountId;
263
+ if (!account) {
264
+ return null;
265
+ }
266
+ const accountResponse = await this.requestAccount(account);
267
+ if (!accountResponse) {
268
+ throw new Error(
269
+ "Failed to fetch account. Try again or check if the Account ID is valid."
270
+ );
271
+ }
272
+ const balance = accountResponse.balance.balance / 10 ** 8;
273
+ return Number(balance).toLocaleString("en-US");
274
+ }
275
+ getAccountInfo() {
276
+ const { accountId: cachedAccountId } = this.loadConnectionInfo();
277
+ if (!cachedAccountId) {
278
+ return null;
279
+ }
280
+ const signers = this?.dAppConnector?.signers;
281
+ if (!signers?.length) {
282
+ return null;
283
+ }
284
+ const cachedSigner = this.dAppConnector.signers.find(
285
+ (signer_) => signer_.getAccountId().toString() === cachedAccountId
286
+ );
287
+ if (!cachedSigner) {
288
+ return null;
289
+ }
290
+ const accountId = cachedSigner?.getAccountId()?.toString();
291
+ if (!accountId) {
292
+ return null;
293
+ }
294
+ const network = cachedSigner.getLedgerId();
295
+ return {
296
+ accountId,
297
+ network
298
+ };
299
+ }
300
+ async createTopic(memo, adminKey, submitKey) {
301
+ this.ensureInitialized();
302
+ let transaction = new TopicCreateTransaction().setTopicMemo(memo || "");
303
+ if (adminKey) {
304
+ const adminWithPrivateKey = PrivateKey.fromString(adminKey);
305
+ transaction.setAdminKey(adminWithPrivateKey.publicKey);
306
+ transaction = await transaction.sign(adminWithPrivateKey);
307
+ }
308
+ if (submitKey) {
309
+ transaction.setSubmitKey(PrivateKey.fromString(submitKey).publicKey);
310
+ }
311
+ const receipt = await this.executeTransaction(transaction);
312
+ return receipt.topicId.toString();
313
+ }
314
+ async createToken(name, symbol, initialSupply, decimals, treasuryAccountId, adminKey, supplyKey) {
315
+ this.ensureInitialized();
316
+ let transaction = new TokenCreateTransaction().setTokenName(name).setTokenSymbol(symbol).setDecimals(decimals).setInitialSupply(initialSupply).setTreasuryAccountId(AccountId.fromString(treasuryAccountId)).setTokenType(TokenType.NonFungibleUnique).setSupplyType(TokenSupplyType.Finite);
317
+ if (supplyKey) {
318
+ transaction = transaction.setSupplyKey(PrivateKey.fromString(supplyKey));
319
+ }
320
+ if (adminKey) {
321
+ transaction = transaction.setAdminKey(PrivateKey.fromString(adminKey));
322
+ transaction = await transaction.sign(PrivateKey.fromString(adminKey));
323
+ }
324
+ const receipt = await this.executeTransaction(transaction);
325
+ return receipt.tokenId.toString();
326
+ }
327
+ async mintNFT(tokenId, metadata, supplyKey) {
328
+ this.ensureInitialized();
329
+ let transaction = await new TokenMintTransaction().setTokenId(tokenId).setMetadata([Buffer.from(metadata, "utf-8")]).sign(supplyKey);
330
+ return this.executeTransaction(transaction);
331
+ }
332
+ async getMessages(topicId, lastTimestamp, disableTimestampFilter = false, network) {
333
+ const networkPrefix = network || this.getNetworkPrefix();
334
+ const baseUrl = `https://${networkPrefix}.mirrornode.hedera.com`;
335
+ const timestampQuery = Number(lastTimestamp) > 0 && !disableTimestampFilter ? `&timestamp=gt:${lastTimestamp}` : "";
336
+ const url = `${baseUrl}/api/v1/topics/${topicId}/messages?limit=200${timestampQuery}`;
337
+ try {
338
+ const response = await fetchWithRetry()(url);
339
+ if (!response.ok) {
340
+ throw new Error(
341
+ `Failed to make request to mirror node: ${response.status}`
342
+ );
343
+ }
344
+ const data = await response.json();
345
+ const messages = data?.messages || [];
346
+ const nextLink = data?.links?.next;
347
+ const collectedMessages = messages.map((msg) => {
348
+ const parsedMessage = JSON.parse(atob(msg.message));
349
+ return {
350
+ ...parsedMessage,
351
+ payer: msg.payer_account_id,
352
+ created: new Date(Number(msg.consensus_timestamp) * 1e3),
353
+ consensus_timestamp: msg.consensus_timestamp,
354
+ sequence_number: msg.sequence_number
355
+ };
356
+ });
357
+ if (nextLink) {
358
+ const nextResult = await this.getMessages(
359
+ topicId,
360
+ Number(
361
+ collectedMessages[collectedMessages.length - 1]?.consensus_timestamp
362
+ ),
363
+ disableTimestampFilter
364
+ );
365
+ collectedMessages.push(...nextResult.messages);
366
+ }
367
+ return {
368
+ messages: collectedMessages.sort(
369
+ (a, b) => a.sequence_number - b.sequence_number
370
+ ),
371
+ error: ""
372
+ };
373
+ } catch (error) {
374
+ this.logger.error("Error fetching topic data:", error);
375
+ return {
376
+ messages: [],
377
+ error: error.toString()
378
+ };
379
+ }
380
+ }
381
+ async signMessage(message) {
382
+ const dAppConnector = this.dAppConnector;
383
+ if (!dAppConnector) {
384
+ throw new Error("No active connection or signer");
385
+ }
386
+ const accountInfo = this.getAccountInfo();
387
+ const accountId = accountInfo?.accountId;
388
+ const params = {
389
+ signerAccountId: `hedera:${this.network}:${accountId}`,
390
+ message
391
+ };
392
+ const result = await dAppConnector.signMessage(
393
+ params
394
+ );
395
+ return { userSignature: result.signatureMap };
396
+ }
397
+ saveConnectionInfo(accountId, connectedNetwork) {
398
+ if (!accountId) {
399
+ localStorage.removeItem("connectedAccountId");
400
+ localStorage.removeItem("connectedNetwork");
401
+ } else {
402
+ const cleanNetwork = connectedNetwork?.replace(/['"]+/g, "");
403
+ localStorage.setItem("connectedNetwork", cleanNetwork);
404
+ localStorage.setItem("connectedAccountId", accountId);
405
+ }
406
+ }
407
+ loadConnectionInfo() {
408
+ return {
409
+ accountId: localStorage.getItem("connectedAccountId"),
410
+ network: localStorage.getItem("connectedNetwork")
411
+ };
412
+ }
413
+ async connectWallet(PROJECT_ID, APP_METADATA, network) {
414
+ try {
415
+ await this.init(PROJECT_ID, APP_METADATA, network);
416
+ const session = await this.connect();
417
+ const accountInfo = this.getAccountInfo();
418
+ const accountId = accountInfo?.accountId;
419
+ const balance = await this.getAccountBalance();
420
+ const networkPrefix = this.getNetworkPrefix();
421
+ this.saveConnectionInfo(accountId, networkPrefix);
422
+ return {
423
+ accountId,
424
+ balance,
425
+ session
426
+ };
427
+ } catch (error) {
428
+ this.logger.error("Failed to connect wallet:", error);
429
+ throw error;
430
+ }
431
+ }
432
+ async disconnectWallet(clearStorage = true) {
433
+ try {
434
+ const success = await this.disconnect();
435
+ if (success && clearStorage) {
436
+ localStorage.clear();
437
+ }
438
+ this.saveConnectionInfo(void 0);
439
+ return success;
440
+ } catch (error) {
441
+ this.logger.error("Failed to disconnect wallet:", error);
442
+ return false;
443
+ }
444
+ }
445
+ async initAccount(PROJECT_ID, APP_METADATA, networkOverride, onSessionIframeCreated = () => {
446
+ }) {
447
+ const { accountId: savedAccountId, network: savedNetwork } = this.loadConnectionInfo();
448
+ if (savedAccountId && savedNetwork) {
449
+ try {
450
+ const defaultNetwork = savedNetwork === "mainnet" ? LedgerId.MAINNET : LedgerId.TESTNET;
451
+ const network = networkOverride || defaultNetwork;
452
+ await this.init(
453
+ PROJECT_ID,
454
+ APP_METADATA,
455
+ network,
456
+ onSessionIframeCreated
457
+ );
458
+ const balance = await this.getAccountBalance();
459
+ return {
460
+ accountId: savedAccountId,
461
+ balance
462
+ };
463
+ } catch (error) {
464
+ this.logger.error("Failed to reconnect:", error);
465
+ this.saveConnectionInfo(void 0, void 0);
466
+ return null;
467
+ }
468
+ } else if (networkOverride) {
469
+ try {
470
+ this.logger.info(
471
+ "initializing normally through override.",
472
+ networkOverride
473
+ );
474
+ await this.init(
475
+ PROJECT_ID,
476
+ APP_METADATA,
477
+ networkOverride,
478
+ onSessionIframeCreated
479
+ );
480
+ this.logger.info("initialized", networkOverride);
481
+ await this.connectViaDappBrowser();
482
+ this.logger.info("connected via dapp browser");
483
+ } catch (error) {
484
+ this.logger.error("Failed to fallback connect:", error);
485
+ this.saveConnectionInfo(void 0, void 0);
486
+ return null;
487
+ }
488
+ }
489
+ return null;
490
+ }
491
+ subscribeToExtensions(callback) {
492
+ if (this.extensionCheckInterval) {
493
+ clearInterval(this.extensionCheckInterval);
494
+ }
495
+ this.hasCalledExtensionCallback = false;
496
+ this.extensionCheckInterval = setInterval(() => {
497
+ const extensions = this.dAppConnector?.extensions || [];
498
+ const availableExtension = extensions.find(
499
+ (ext) => ext.availableInIframe
500
+ );
501
+ if (availableExtension && !this.hasCalledExtensionCallback) {
502
+ this.hasCalledExtensionCallback = true;
503
+ callback(availableExtension);
504
+ if (this.extensionCheckInterval) {
505
+ clearInterval(this.extensionCheckInterval);
506
+ this.extensionCheckInterval = null;
507
+ }
508
+ }
509
+ }, 1e3);
510
+ return () => {
511
+ if (this.extensionCheckInterval) {
512
+ clearInterval(this.extensionCheckInterval);
513
+ this.extensionCheckInterval = null;
514
+ }
515
+ this.hasCalledExtensionCallback = false;
516
+ };
517
+ }
518
+ async connectViaDappBrowser() {
519
+ const extensions = this.dAppConnector.extensions || [];
520
+ const extension = extensions.find((ext) => {
521
+ this.logger.info("Checking extension", ext);
522
+ return ext.availableInIframe;
523
+ });
524
+ this.logger.info("extensions are", extensions, extension);
525
+ if (extension) {
526
+ await this.connectToExtension(extension);
527
+ } else {
528
+ this.subscribeToExtensions(async (newExtension) => {
529
+ await this.connectToExtension(newExtension);
530
+ });
531
+ }
532
+ }
533
+ async connectToExtension(extension) {
534
+ this.logger.info("found extension, connecting to iframe.", extension);
535
+ const session = await this.dAppConnector.connectExtension(extension.id);
536
+ const onSessionIframeCreated = this.dAppConnector.onSessionIframeCreated;
537
+ if (onSessionIframeCreated) {
538
+ onSessionIframeCreated(session);
539
+ }
540
+ }
541
+ ensureInitialized() {
542
+ if (!this.dAppConnector) {
543
+ throw new Error("SDK not initialized. Call init() first.");
544
+ }
545
+ }
546
+ static run() {
547
+ try {
548
+ if (typeof window !== "undefined") {
549
+ window.HashinalsWalletConnectSDK = HashinalsWalletConnectSDK.getInstance();
550
+ window.HashgraphSDK = HashgraphSDK;
551
+ }
552
+ } catch (e) {
553
+ console.error("[ERROR]: failed setting sdk on window");
554
+ }
555
+ }
556
+ async transferToken(tokenId, fromAccountId, toAccountId, amount) {
557
+ this.ensureInitialized();
558
+ const transaction = new TransferTransaction().setTransactionId(TransactionId.generate(fromAccountId)).addTokenTransfer(
559
+ TokenId.fromString(tokenId),
560
+ AccountId.fromString(fromAccountId),
561
+ -amount
562
+ ).addTokenTransfer(
563
+ TokenId.fromString(tokenId),
564
+ AccountId.fromString(toAccountId),
565
+ amount
566
+ );
567
+ return this.executeTransaction(transaction);
568
+ }
569
+ async createAccount(initialBalance) {
570
+ this.ensureInitialized();
571
+ const transaction = new AccountCreateTransaction().setInitialBalance(
572
+ new Hbar(initialBalance)
573
+ );
574
+ return this.executeTransaction(transaction);
575
+ }
576
+ async associateTokenToAccount(accountId, tokenId) {
577
+ this.ensureInitialized();
578
+ const transaction = new TokenAssociateTransaction().setAccountId(AccountId.fromString(accountId)).setTokenIds([TokenId.fromString(tokenId)]);
579
+ return this.executeTransaction(transaction);
580
+ }
581
+ async dissociateTokenFromAccount(accountId, tokenId) {
582
+ this.ensureInitialized();
583
+ const transaction = new TokenDissociateTransaction().setAccountId(AccountId.fromString(accountId)).setTokenIds([TokenId.fromString(tokenId)]);
584
+ return this.executeTransaction(transaction);
585
+ }
586
+ async updateAccount(accountId, maxAutomaticTokenAssociations) {
587
+ this.ensureInitialized();
588
+ const transaction = new AccountUpdateTransaction().setAccountId(AccountId.fromString(accountId)).setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociations);
589
+ return this.executeTransaction(transaction);
590
+ }
591
+ async approveAllowance(spenderAccountId, tokenId, amount, ownerAccountId) {
592
+ this.ensureInitialized();
593
+ const transaction = new AccountAllowanceApproveTransaction().approveTokenAllowance(
594
+ TokenId.fromString(tokenId),
595
+ AccountId.fromString(ownerAccountId),
596
+ AccountId.fromString(spenderAccountId),
597
+ amount
598
+ );
599
+ return this.executeTransaction(transaction);
600
+ }
601
+ async getAccountTokens(accountId) {
602
+ this.ensureInitialized();
603
+ const networkPrefix = this.getNetworkPrefix();
604
+ const baseUrl = `https://${networkPrefix}.mirrornode.hedera.com`;
605
+ const url = `${baseUrl}/api/v1/accounts/${accountId}/tokens?limit=200`;
606
+ try {
607
+ const response = await fetchWithRetry()(url);
608
+ if (!response.ok) {
609
+ throw new Error(
610
+ `Failed to make request to mirror node for account tokens: ${response.status}`
611
+ );
612
+ }
613
+ const data = await response.json();
614
+ const tokens = [];
615
+ for (const token of data.tokens) {
616
+ if (token.token_id) {
617
+ tokens.push({
618
+ tokenId: token.token_id,
619
+ balance: token.balance,
620
+ decimals: token.decimals,
621
+ formatted_balance: (token.balance / 10 ** token.decimals).toLocaleString("en-US"),
622
+ created_timestamp: new Date(Number(token.created_timestamp) * 1e3)
623
+ });
624
+ }
625
+ }
626
+ let nextLink = data.links?.next;
627
+ while (nextLink) {
628
+ const nextUrl = `${baseUrl}${nextLink}`;
629
+ const nextResponse = await fetchWithRetry()(nextUrl);
630
+ if (!nextResponse.ok) {
631
+ throw new Error(
632
+ `Failed to make request to mirror node for account tokens: ${nextResponse.status}, page: ${nextUrl}`
633
+ );
634
+ }
635
+ const nextData = await nextResponse.json();
636
+ for (const token of nextData.tokens) {
637
+ if (token.token_id) {
638
+ tokens.push({
639
+ tokenId: token.token_id,
640
+ balance: token.balance,
641
+ decimals: token.decimals,
642
+ formatted_balance: (token.balance / 10 ** token.decimals).toLocaleString("en-US"),
643
+ created_timestamp: new Date(
644
+ Number(token.created_timestamp) * 1e3
645
+ )
646
+ });
647
+ }
648
+ }
649
+ nextLink = nextData.links?.next;
650
+ }
651
+ return { tokens };
652
+ } catch (error) {
653
+ this.logger.error("Error fetching account tokens:", error);
654
+ throw error;
655
+ }
656
+ }
657
+ async getTransaction(transactionId) {
658
+ try {
659
+ const networkPrefix = this.getNetworkPrefix();
660
+ const url = `https://${networkPrefix}.mirrornode.hedera.com/api/v1/transactions/${transactionId}`;
661
+ this.logger.debug("Fetching transaction", url);
662
+ const request = await fetchWithRetry()(url);
663
+ if (!request.ok) {
664
+ throw new Error(`Failed to fetch transaction: ${request.status}`);
665
+ }
666
+ return await request.json();
667
+ } catch (e) {
668
+ this.logger.error("Failed to get transaction", e);
669
+ return null;
670
+ }
671
+ }
672
+ async getTransactionByTimestamp(timestamp) {
673
+ try {
674
+ const networkPrefix = this.getNetworkPrefix();
675
+ const url = `https://${networkPrefix}.mirrornode.hedera.com/api/v1/transactions?timestamp=${timestamp}`;
676
+ this.logger.debug("Fetching transaction by timestamp", url);
677
+ const request = await fetchWithRetry()(url);
678
+ if (!request.ok) {
679
+ throw new Error(
680
+ `Failed to fetch transaction by timestamp: ${request.status}`
681
+ );
682
+ }
683
+ const response = await request.json();
684
+ const transaction = response?.transactions?.[0];
685
+ if (transaction) {
686
+ return await this.getTransaction(transaction.transaction_id);
687
+ }
688
+ return null;
689
+ } catch (e) {
690
+ this.logger.error("Failed to get transaction by timestamp", e);
691
+ return null;
692
+ }
693
+ }
694
+ async getAccountNFTs(accountId, tokenId) {
695
+ try {
696
+ const networkPrefix = this.getNetworkPrefix();
697
+ const tokenQuery = tokenId ? `&token.id=${tokenId}` : "";
698
+ const url = `https://${networkPrefix}.mirrornode.hedera.com/api/v1/accounts/${accountId}/nfts?limit=200${tokenQuery}`;
699
+ const request = await fetchWithRetry()(url);
700
+ if (!request.ok) {
701
+ throw new Error(`Failed to fetch NFTs for account: ${request.status}`);
702
+ }
703
+ const response = await request.json();
704
+ let nextLink = response?.links?.next || null;
705
+ let nfts = response.nfts || [];
706
+ while (nextLink) {
707
+ try {
708
+ const nextRequest = await fetchWithRetry()(
709
+ `https://${networkPrefix}.mirrornode.hedera.com${nextLink}`
710
+ );
711
+ if (!nextRequest.ok) {
712
+ throw new Error(
713
+ `Failed to fetch next page of NFTs: ${nextRequest.status}`
714
+ );
715
+ }
716
+ const nextResponse = await nextRequest.json();
717
+ const nextNfts = nextResponse?.nfts || [];
718
+ nfts = [...nfts, ...nextNfts];
719
+ nextLink = nextResponse?.links?.next && nextLink !== nextResponse?.links?.next ? nextResponse.links.next : null;
720
+ } catch (e) {
721
+ this.logger.error("Failed to fetch next page of NFTs", e);
722
+ break;
723
+ }
724
+ }
725
+ return nfts.map((nft) => {
726
+ try {
727
+ nft.token_uri = Buffer.from(nft.metadata, "base64").toString("ascii");
728
+ } catch (e) {
729
+ this.logger.error("Failed to decode NFT metadata", e);
730
+ }
731
+ return nft;
732
+ });
733
+ } catch (e) {
734
+ this.logger.error("Failed to get account NFTs", e);
735
+ return [];
736
+ }
737
+ }
738
+ async validateNFTOwnership(serialNumber, accountId, tokenId) {
739
+ const userNFTs = await this.getAccountNFTs(accountId, tokenId);
740
+ return userNFTs.find(
741
+ (nft) => nft.token_id === tokenId && nft.serial_number.toString() === serialNumber
742
+ ) || null;
743
+ }
744
+ async readSmartContract(data, fromAccount, contractId, estimate = true, value = 0) {
745
+ try {
746
+ const networkPrefix = this.getNetworkPrefix();
747
+ const body = {
748
+ block: "latest",
749
+ data,
750
+ estimate,
751
+ from: fromAccount.toSolidityAddress(),
752
+ to: contractId.toSolidityAddress(),
753
+ value
754
+ };
755
+ if (!estimate) {
756
+ body.gas = 3e5;
757
+ body.gasPrice = 1e8;
758
+ }
759
+ const url = `https://${networkPrefix}.mirrornode.hedera.com/api/v1/contracts/call`;
760
+ const response = await fetchWithRetry()(url, {
761
+ method: "POST",
762
+ body: JSON.stringify(body),
763
+ headers: {
764
+ "Content-Type": "application/json"
765
+ }
766
+ });
767
+ if (!response.ok) {
768
+ throw new Error(`Failed to make contract call: ${response.status}`);
769
+ }
770
+ return await response.json();
771
+ } catch (e) {
772
+ this.logger.error("Failed to make contract call", e);
773
+ return null;
774
+ }
775
+ }
776
+ }
777
+ export {
778
+ HashgraphSDK,
779
+ HashinalsWalletConnectSDK,
780
+ Name,
781
+ Result,
782
+ base64StringToSignatureMap,
783
+ prefixMessageToSign,
784
+ verifyMessageSignature
785
+ };
786
+ //# sourceMappingURL=index.js.map