@ledgerhq/coin-hedera 1.16.0-nightly.20251218023953 → 1.16.0-nightly.20251219024040

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +12 -10
  2. package/lib/constants.d.ts +4 -0
  3. package/lib/constants.d.ts.map +1 -1
  4. package/lib/constants.js +12 -1
  5. package/lib/constants.js.map +1 -1
  6. package/lib/logic/getBlock.d.ts.map +1 -1
  7. package/lib/logic/getBlock.js +4 -1
  8. package/lib/logic/getBlock.js.map +1 -1
  9. package/lib/logic/utils.d.ts +27 -0
  10. package/lib/logic/utils.d.ts.map +1 -1
  11. package/lib/logic/utils.js +51 -3
  12. package/lib/logic/utils.js.map +1 -1
  13. package/lib/network/api.d.ts +5 -1
  14. package/lib/network/api.d.ts.map +1 -1
  15. package/lib/network/api.js +4 -3
  16. package/lib/network/api.js.map +1 -1
  17. package/lib/test/fixtures/account.fixture.d.ts +3 -0
  18. package/lib/test/fixtures/account.fixture.d.ts.map +1 -1
  19. package/lib/test/fixtures/account.fixture.js +3 -0
  20. package/lib/test/fixtures/account.fixture.js.map +1 -1
  21. package/lib-es/constants.d.ts +4 -0
  22. package/lib-es/constants.d.ts.map +1 -1
  23. package/lib-es/constants.js +11 -0
  24. package/lib-es/constants.js.map +1 -1
  25. package/lib-es/logic/getBlock.d.ts.map +1 -1
  26. package/lib-es/logic/getBlock.js +4 -1
  27. package/lib-es/logic/getBlock.js.map +1 -1
  28. package/lib-es/logic/utils.d.ts +27 -0
  29. package/lib-es/logic/utils.d.ts.map +1 -1
  30. package/lib-es/logic/utils.js +50 -3
  31. package/lib-es/logic/utils.js.map +1 -1
  32. package/lib-es/network/api.d.ts +5 -1
  33. package/lib-es/network/api.d.ts.map +1 -1
  34. package/lib-es/network/api.js +4 -3
  35. package/lib-es/network/api.js.map +1 -1
  36. package/lib-es/test/fixtures/account.fixture.d.ts +3 -0
  37. package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -1
  38. package/lib-es/test/fixtures/account.fixture.js +3 -0
  39. package/lib-es/test/fixtures/account.fixture.js.map +1 -1
  40. package/package.json +11 -10
  41. package/src/api/index.integ.test.ts +48 -6
  42. package/src/constants.ts +12 -0
  43. package/src/logic/getBlock.test.ts +4 -4
  44. package/src/logic/getBlock.ts +4 -1
  45. package/src/logic/utils.test.ts +178 -8
  46. package/src/logic/utils.ts +64 -2
  47. package/src/network/api.test.ts +36 -17
  48. package/src/network/api.ts +12 -6
  49. package/src/test/fixtures/account.fixture.ts +3 -0
@@ -9,6 +9,7 @@ import {
9
9
  HEDERA_OPERATION_TYPES,
10
10
  HEDERA_TRANSACTION_MODES,
11
11
  SYNTHETIC_BLOCK_WINDOW_SECONDS,
12
+ OP_TYPES_EXCLUDING_FEES,
12
13
  } from "../constants";
13
14
  import { HederaRecipientInvalidChecksum } from "../errors";
14
15
  import { apiClient } from "../network/api";
@@ -64,6 +65,7 @@ import {
64
65
  getOperationDetailsExtraFields,
65
66
  calculateAPY,
66
67
  analyzeStakingOperation,
68
+ calculateUncommittedBalanceChange,
67
69
  } from "./utils";
68
70
 
69
71
  jest.mock("../network/api");
@@ -152,14 +154,16 @@ describe("logic utils", () => {
152
154
  expect(getOperationValue({ asset: tokenAsset, operation })).toBe(BigInt(0));
153
155
  });
154
156
 
155
- it("should substract fee from value for native OUT operations", () => {
156
- const operation = getMockedOperation({
157
- type: "OUT",
158
- value: BigNumber(1000),
159
- fee: BigNumber(100),
160
- });
157
+ it("should subtract fee from native operations that exclude fees", () => {
158
+ OP_TYPES_EXCLUDING_FEES.forEach(type => {
159
+ const operation = getMockedOperation({
160
+ type,
161
+ value: BigNumber(1000),
162
+ fee: BigNumber(100),
163
+ });
161
164
 
162
- expect(getOperationValue({ asset: nativeAsset, operation })).toBe(BigInt(900));
165
+ expect(getOperationValue({ asset: nativeAsset, operation })).toBe(BigInt(900));
166
+ });
163
167
  });
164
168
 
165
169
  it("should return value for other operations", () => {
@@ -970,6 +974,123 @@ describe("logic utils", () => {
970
974
  });
971
975
  });
972
976
 
977
+ describe("calculateUncommittedBalanceChange", () => {
978
+ const mockAddress = "0.0.12345";
979
+ const mockStartTimestamp = "1762202064.065172388";
980
+ const mockEndTimestamp = "1762202074.065172388";
981
+
982
+ beforeEach(() => {
983
+ jest.clearAllMocks();
984
+ });
985
+
986
+ it("should return 0 when there are no transactions in the time range", async () => {
987
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce([]);
988
+
989
+ const result = await calculateUncommittedBalanceChange({
990
+ address: mockAddress,
991
+ startTimestamp: mockStartTimestamp,
992
+ endTimestamp: mockEndTimestamp,
993
+ });
994
+
995
+ expect(result).toEqual(new BigNumber(0));
996
+ expect(apiClient.getTransactionsByTimestampRange).toHaveBeenCalledTimes(1);
997
+ expect(apiClient.getTransactionsByTimestampRange).toHaveBeenCalledWith({
998
+ address: mockAddress,
999
+ startTimestamp: `gt:${mockStartTimestamp}`,
1000
+ endTimestamp: `lte:${mockEndTimestamp}`,
1001
+ });
1002
+ });
1003
+
1004
+ it("should calculate balance change with mixed incoming and outgoing transfers", async () => {
1005
+ const mockTransactions = [
1006
+ {
1007
+ consensus_timestamp: "1762202065.000000000",
1008
+ transfers: [
1009
+ { account: mockAddress, amount: 2000 },
1010
+ { account: "0.0.98", amount: -2000 },
1011
+ ],
1012
+ },
1013
+ {
1014
+ consensus_timestamp: "1762202070.000000000",
1015
+ transfers: [
1016
+ { account: mockAddress, amount: -500 },
1017
+ { account: "0.0.99", amount: 500 },
1018
+ ],
1019
+ },
1020
+ {
1021
+ consensus_timestamp: "1762202072.000000000",
1022
+ transfers: [
1023
+ { account: mockAddress, amount: 300 },
1024
+ { account: "0.0.100", amount: -300 },
1025
+ ],
1026
+ },
1027
+ ] as HederaMirrorTransaction[];
1028
+
1029
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce(
1030
+ mockTransactions,
1031
+ );
1032
+
1033
+ const result = await calculateUncommittedBalanceChange({
1034
+ address: mockAddress,
1035
+ startTimestamp: mockStartTimestamp,
1036
+ endTimestamp: mockEndTimestamp,
1037
+ });
1038
+
1039
+ expect(result).toEqual(new BigNumber(1800)); // 2000 - 500 + 300
1040
+ });
1041
+
1042
+ it("should ignore transfers for other accounts", async () => {
1043
+ const mockTransactions = [
1044
+ {
1045
+ consensus_timestamp: "1762202065.000000000",
1046
+ transfers: [
1047
+ { account: "0.0.98", amount: 5000 },
1048
+ { account: "0.0.99", amount: -5000 },
1049
+ ],
1050
+ },
1051
+ {
1052
+ consensus_timestamp: "1762202070.000000000",
1053
+ transfers: [
1054
+ { account: mockAddress, amount: 1000 },
1055
+ { account: "0.0.100", amount: -1000 },
1056
+ ],
1057
+ },
1058
+ ] as HederaMirrorTransaction[];
1059
+
1060
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce(
1061
+ mockTransactions,
1062
+ );
1063
+
1064
+ const result = await calculateUncommittedBalanceChange({
1065
+ address: mockAddress,
1066
+ startTimestamp: mockStartTimestamp,
1067
+ endTimestamp: mockEndTimestamp,
1068
+ });
1069
+
1070
+ expect(result).toEqual(new BigNumber(1000));
1071
+ });
1072
+
1073
+ it("should return 0 when timestamps are equal or invalid", async () => {
1074
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce([]);
1075
+
1076
+ const [resultEqual, resultInvalid] = await Promise.all([
1077
+ calculateUncommittedBalanceChange({
1078
+ address: mockAddress,
1079
+ startTimestamp: mockStartTimestamp,
1080
+ endTimestamp: mockStartTimestamp,
1081
+ }),
1082
+ calculateUncommittedBalanceChange({
1083
+ address: mockAddress,
1084
+ startTimestamp: mockEndTimestamp,
1085
+ endTimestamp: mockStartTimestamp,
1086
+ }),
1087
+ ]);
1088
+
1089
+ expect(resultEqual).toEqual(new BigNumber(0));
1090
+ expect(resultInvalid).toEqual(new BigNumber(0));
1091
+ });
1092
+ });
1093
+
973
1094
  describe("analyzeStakingOperation", () => {
974
1095
  const mockAddress = "0.0.12345";
975
1096
  const mockTimestamp = "1762202064.065172388";
@@ -979,13 +1100,14 @@ describe("logic utils", () => {
979
1100
  } as HederaMirrorTransaction;
980
1101
 
981
1102
  beforeEach(() => {
982
- jest.clearAllMocks();
1103
+ jest.resetAllMocks();
983
1104
  });
984
1105
 
985
1106
  it("detects DELEGATE operation when staking starts", async () => {
986
1107
  const accountBefore = getMockedMirrorAccount({ staked_node_id: null });
987
1108
  const accountAfter = getMockedMirrorAccount({ staked_node_id: 5 });
988
1109
 
1110
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce([]);
989
1111
  (apiClient.getAccount as jest.Mock)
990
1112
  .mockResolvedValueOnce(accountBefore)
991
1113
  .mockResolvedValueOnce(accountAfter);
@@ -998,6 +1120,7 @@ describe("logic utils", () => {
998
1120
  targetStakingNodeId: 5,
999
1121
  stakedAmount: BigInt(1000),
1000
1122
  });
1123
+ expect(apiClient.getAccount).toHaveBeenCalledTimes(2);
1001
1124
  expect(apiClient.getAccount).toHaveBeenCalledWith(mockAddress, `lt:${mockTimestamp}`);
1002
1125
  expect(apiClient.getAccount).toHaveBeenCalledWith(mockAddress, `eq:${mockTimestamp}`);
1003
1126
  });
@@ -1006,6 +1129,7 @@ describe("logic utils", () => {
1006
1129
  const accountBefore = getMockedMirrorAccount({ staked_node_id: 5 });
1007
1130
  const accountAfter = getMockedMirrorAccount({ staked_node_id: null });
1008
1131
 
1132
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce([]);
1009
1133
  (apiClient.getAccount as jest.Mock)
1010
1134
  .mockResolvedValueOnce(accountBefore)
1011
1135
  .mockResolvedValueOnce(accountAfter);
@@ -1024,6 +1148,7 @@ describe("logic utils", () => {
1024
1148
  const accountBefore = getMockedMirrorAccount({ staked_node_id: 3 });
1025
1149
  const accountAfter = getMockedMirrorAccount({ staked_node_id: 10 });
1026
1150
 
1151
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce([]);
1027
1152
  (apiClient.getAccount as jest.Mock)
1028
1153
  .mockResolvedValueOnce(accountBefore)
1029
1154
  .mockResolvedValueOnce(accountAfter);
@@ -1038,6 +1163,51 @@ describe("logic utils", () => {
1038
1163
  });
1039
1164
  });
1040
1165
 
1166
+ it("calculates correct staked amount with uncommitted transactions", async () => {
1167
+ const mockBalance = { balance: 1000000, timestamp: "1762202060.000000000", tokens: [] };
1168
+ const mockAccountBefore = getMockedMirrorAccount({
1169
+ account: mockAddress,
1170
+ staked_node_id: null,
1171
+ balance: mockBalance,
1172
+ });
1173
+ const mockAccountAfter = getMockedMirrorAccount({
1174
+ account: mockAddress,
1175
+ staked_node_id: 5,
1176
+ balance: mockBalance,
1177
+ });
1178
+ const mockTransactionsMissingInBalance = [
1179
+ {
1180
+ consensus_timestamp: `${Math.floor(Number(mockBalance.timestamp)) + 5}.000000000`,
1181
+ transfers: [
1182
+ { account: mockAddress, amount: -100000 },
1183
+ { account: "0.0.98", amount: 100000 },
1184
+ ],
1185
+ },
1186
+ ] as HederaMirrorTransaction[];
1187
+
1188
+ (apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValueOnce(
1189
+ mockTransactionsMissingInBalance,
1190
+ );
1191
+ (apiClient.getAccount as jest.Mock)
1192
+ .mockResolvedValueOnce(mockAccountBefore)
1193
+ .mockResolvedValueOnce(mockAccountAfter);
1194
+
1195
+ const result = await analyzeStakingOperation(mockAddress, mockTx);
1196
+
1197
+ expect(apiClient.getTransactionsByTimestampRange).toHaveBeenCalledTimes(1);
1198
+ expect(apiClient.getTransactionsByTimestampRange).toHaveBeenCalledWith({
1199
+ address: mockAddress,
1200
+ startTimestamp: `gt:${mockAccountBefore.balance.timestamp}`,
1201
+ endTimestamp: `lte:${mockTimestamp}`,
1202
+ });
1203
+ expect(result).toEqual({
1204
+ operationType: "DELEGATE",
1205
+ previousStakingNodeId: null,
1206
+ targetStakingNodeId: 5,
1207
+ stakedAmount: BigInt(900000),
1208
+ });
1209
+ });
1210
+
1041
1211
  it("returns null for regular account update (both null)", async () => {
1042
1212
  const accountBefore = getMockedMirrorAccount({ staked_node_id: null });
1043
1213
  const accountAfter = getMockedMirrorAccount({ staked_node_id: null });
@@ -22,6 +22,7 @@ import {
22
22
  HEDERA_TRANSACTION_MODES,
23
23
  SYNTHETIC_BLOCK_WINDOW_SECONDS,
24
24
  TINYBAR_SCALE,
25
+ OP_TYPES_EXCLUDING_FEES,
25
26
  } from "../constants";
26
27
  import { apiClient } from "../network/api";
27
28
  import type {
@@ -69,7 +70,7 @@ export const getOperationValue = ({
69
70
  return BigInt(0);
70
71
  }
71
72
 
72
- if (asset.type === "native" && operation.type === "OUT") {
73
+ if (asset.type === "native" && OP_TYPES_EXCLUDING_FEES.includes(operation.type)) {
73
74
  return BigInt(operation.value.toFixed(0)) - BigInt(operation.fee.toFixed(0));
74
75
  }
75
76
 
@@ -504,6 +505,48 @@ export const calculateAPY = (rewardRateStart: number): number => {
504
505
  return annualRate;
505
506
  };
506
507
 
508
+ /**
509
+ * Calculates the uncommitted balance change for an account between two timestamps.
510
+ *
511
+ * This function handles the timing mismatch between Mirror Node balance snapshots and actual transactions.
512
+ * Balance snapshots are taken at regular intervals, not at every transaction, so querying by exact timestamp
513
+ * may return a snapshot from before moment you need.
514
+ *
515
+ * @param address - Hedera account ID (e.g., "0.0.12345")
516
+ * @param startTimestamp - Start of the time range (exclusive, format: "1234567890.123456789")
517
+ * @param endTimestamp - End of the time range (inclusive, format: "1234567890.123456789")
518
+ * @returns The net balance change as BigInt (sum of all transfers to/from the account)
519
+ */
520
+ export const calculateUncommittedBalanceChange = async ({
521
+ address,
522
+ startTimestamp,
523
+ endTimestamp,
524
+ }: {
525
+ address: string;
526
+ startTimestamp: string;
527
+ endTimestamp: string;
528
+ }): Promise<BigNumber> => {
529
+ if (Number(startTimestamp) >= Number(endTimestamp)) {
530
+ return new BigNumber(0);
531
+ }
532
+
533
+ const uncommittedTransactions = await apiClient.getTransactionsByTimestampRange({
534
+ address,
535
+ startTimestamp: `gt:${startTimestamp}`,
536
+ endTimestamp: `lte:${endTimestamp}`,
537
+ });
538
+
539
+ // Sum all balance changes from transfers related to this account
540
+ const uncommittedBalanceChange = uncommittedTransactions.reduce((total, tx) => {
541
+ const transfers = tx.transfers ?? [];
542
+ const relevantTransfers = transfers.filter(t => t.account === address);
543
+ const netChange = relevantTransfers.reduce((sum, t) => sum.plus(t.amount), new BigNumber(0));
544
+ return total.plus(netChange);
545
+ }, new BigNumber(0));
546
+
547
+ return uncommittedBalanceChange;
548
+ };
549
+
507
550
  /**
508
551
  * Hedera uses the AccountUpdateTransaction for multiple purposes, including staking operations.
509
552
  * Mirror node classifies all such transactions under the same name: "CRYPTOUPDATEACCOUNT".
@@ -517,6 +560,16 @@ export const calculateAPY = (rewardRateStart: number): number => {
517
560
  * 1. Fetching the account state BEFORE the transaction (using lt: timestamp filter)
518
561
  * 2. Fetching the account state AFTER the transaction (using eq: timestamp filter)
519
562
  * 3. Comparing the staked_node_id field to determine what changed
563
+ * 4. Calculating the actual staked amount by replaying uncommitted transactions between
564
+ * the latest balance snapshot and the staking operation to handle snapshot timing mismatches
565
+ *
566
+ * @performance
567
+ * Makes 3 API calls per operation:
568
+ * - account state before
569
+ * - account state after
570
+ * - transaction history based on latest balance snapshot
571
+ *
572
+ * Batching would complicate code for minimal gain given low staking op frequency.
520
573
  */
521
574
  export const analyzeStakingOperation = async (
522
575
  address: string,
@@ -552,10 +605,19 @@ export const analyzeStakingOperation = async (
552
605
  return null;
553
606
  }
554
607
 
608
+ // calculate uncommitted balance changes between the last snapshot and the staking tx
609
+ const uncommittedBalanceChange = await calculateUncommittedBalanceChange({
610
+ address,
611
+ startTimestamp: accountAfter.balance.timestamp,
612
+ endTimestamp: mirrorTx.consensus_timestamp,
613
+ });
614
+
615
+ const actualStakedAmount = uncommittedBalanceChange.plus(accountAfter.balance.balance);
616
+
555
617
  return {
556
618
  operationType,
557
619
  previousStakingNodeId,
558
620
  targetStakingNodeId,
559
- stakedAmount: BigInt(accountAfter.balance.balance), // always entire balance on Hedera (fully liquid)
621
+ stakedAmount: BigInt(actualStakedAmount.toString()), // always entire balance on Hedera (fully liquid)
560
622
  };
561
623
  };
@@ -425,14 +425,33 @@ describe("getTransactionsByTimestampRange", () => {
425
425
  jest.resetAllMocks();
426
426
  });
427
427
 
428
+ it("should include account.id query param if address is provided", async () => {
429
+ mockedNetwork.mockResolvedValueOnce(
430
+ getMockResponse({ transactions: [], links: { next: null } }),
431
+ );
432
+
433
+ await apiClient.getTransactionsByTimestampRange({
434
+ address: "0.0.1234",
435
+ startTimestamp: "gte:1000.000000000",
436
+ endTimestamp: "lt:2000.000000000",
437
+ });
438
+
439
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
440
+ expect(requestUrl).toContain("account.id=0.0.1234");
441
+ });
442
+
428
443
  it("should include correct query params with timestamp range", async () => {
429
444
  mockedNetwork.mockResolvedValueOnce(
430
445
  getMockResponse({ transactions: [], links: { next: null } }),
431
446
  );
432
447
 
433
- await apiClient.getTransactionsByTimestampRange("1000.000000000", "2000.000000000");
448
+ await apiClient.getTransactionsByTimestampRange({
449
+ startTimestamp: "gte:1000.000000000",
450
+ endTimestamp: "lt:2000.000000000",
451
+ });
434
452
 
435
453
  const requestUrl = mockedNetwork.mock.calls[0][0].url;
454
+ expect(requestUrl).not.toContain("account.id=");
436
455
  expect(requestUrl).toContain("timestamp=gte%3A1000.000000000");
437
456
  expect(requestUrl).toContain("timestamp=lt%3A2000.000000000");
438
457
  expect(requestUrl).toContain("limit=100");
@@ -444,10 +463,10 @@ describe("getTransactionsByTimestampRange", () => {
444
463
  getMockResponse({ transactions: [], links: { next: null } }),
445
464
  );
446
465
 
447
- const result = await apiClient.getTransactionsByTimestampRange(
448
- "1000.000000000",
449
- "2000.000000000",
450
- );
466
+ const result = await apiClient.getTransactionsByTimestampRange({
467
+ startTimestamp: "gte:1000.000000000",
468
+ endTimestamp: "lt:2000.000000000",
469
+ });
451
470
 
452
471
  expect(result).toEqual([]);
453
472
  expect(mockedNetwork).toHaveBeenCalledTimes(1);
@@ -464,10 +483,10 @@ describe("getTransactionsByTimestampRange", () => {
464
483
  }),
465
484
  );
466
485
 
467
- const result = await apiClient.getTransactionsByTimestampRange(
468
- "1000.000000000",
469
- "2000.000000000",
470
- );
486
+ const result = await apiClient.getTransactionsByTimestampRange({
487
+ startTimestamp: "gte:1000.000000000",
488
+ endTimestamp: "lt:2000.000000000",
489
+ });
471
490
 
472
491
  expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1500.123456789", "1750.987654321"]);
473
492
  expect(mockedNetwork).toHaveBeenCalledTimes(1);
@@ -494,10 +513,10 @@ describe("getTransactionsByTimestampRange", () => {
494
513
  }),
495
514
  );
496
515
 
497
- const result = await apiClient.getTransactionsByTimestampRange(
498
- "1000.000000000",
499
- "2000.000000000",
500
- );
516
+ const result = await apiClient.getTransactionsByTimestampRange({
517
+ startTimestamp: "gte:1000.000000000",
518
+ endTimestamp: "lt:2000.000000000",
519
+ });
501
520
 
502
521
  expect(result.map(tx => tx.consensus_timestamp)).toEqual([
503
522
  "1100.000000000",
@@ -528,10 +547,10 @@ describe("getTransactionsByTimestampRange", () => {
528
547
  }),
529
548
  );
530
549
 
531
- const result = await apiClient.getTransactionsByTimestampRange(
532
- "1000.000000000",
533
- "2000.000000000",
534
- );
550
+ const result = await apiClient.getTransactionsByTimestampRange({
551
+ startTimestamp: "gte:1000.000000000",
552
+ endTimestamp: "lt:2000.000000000",
553
+ });
535
554
 
536
555
  expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1100.000000000", "1300.000000000"]);
537
556
  expect(mockedNetwork).toHaveBeenCalledTimes(3);
@@ -262,18 +262,24 @@ async function estimateContractCallGas(
262
262
  return new BigNumber(res.data.result);
263
263
  }
264
264
 
265
- async function getTransactionsByTimestampRange(
266
- startTimestamp: string,
267
- endTimestamp: string,
268
- ): Promise<HederaMirrorTransaction[]> {
265
+ async function getTransactionsByTimestampRange({
266
+ address,
267
+ startTimestamp,
268
+ endTimestamp,
269
+ }: {
270
+ address?: string;
271
+ startTimestamp: `${string}:${string}`;
272
+ endTimestamp: `${string}:${string}`;
273
+ }): Promise<HederaMirrorTransaction[]> {
269
274
  const transactions: HederaMirrorTransaction[] = [];
270
275
  const params = new URLSearchParams({
271
276
  limit: "100",
272
277
  order: "desc",
278
+ ...(address && { "account.id": address }),
273
279
  });
274
280
 
275
- params.append("timestamp", `gte:${startTimestamp}`);
276
- params.append("timestamp", `lt:${endTimestamp}`);
281
+ params.append("timestamp", startTimestamp);
282
+ params.append("timestamp", endTimestamp);
277
283
 
278
284
  let nextPath: string | null = `/api/v1/transactions?${params.toString()}`;
279
285
 
@@ -140,6 +140,9 @@ export const MAINNET_TEST_ACCOUNTS = {
140
140
  associatedTokenWithoutBalance: "0.0.7243470",
141
141
  notAssociatedToken: "0.0.3176721",
142
142
  },
143
+ withQuickBalanceChanges: {
144
+ accountId: "0.0.10176637",
145
+ },
143
146
  activeStaking: {
144
147
  accountId: "0.0.8835924",
145
148
  publicKey: "34e26415574250721e8869bd33ea2678c2bbccff5fc70bd8b0ec9239295fd2cf",