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

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