@ledgerhq/coin-xrp 0.9.2 → 1.0.0-nightly.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.
Files changed (144) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/CHANGELOG.md +11 -10
  3. package/lib/api/index.d.ts.map +1 -1
  4. package/lib/api/index.integ.test.js +35 -36
  5. package/lib/api/index.integ.test.js.map +1 -1
  6. package/lib/api/index.js +65 -50
  7. package/lib/api/index.js.map +1 -1
  8. package/lib/api/index.test.js +66 -44
  9. package/lib/api/index.test.js.map +1 -1
  10. package/lib/bridge/broadcast.js +3 -12
  11. package/lib/bridge/broadcast.js.map +1 -1
  12. package/lib/bridge/estimateMaxSpendable.js +9 -14
  13. package/lib/bridge/estimateMaxSpendable.js.map +1 -1
  14. package/lib/bridge/getTransactionStatus.js +4 -13
  15. package/lib/bridge/getTransactionStatus.js.map +1 -1
  16. package/lib/bridge/prepareTransaction.js +4 -13
  17. package/lib/bridge/prepareTransaction.js.map +1 -1
  18. package/lib/bridge/signOperation.js +60 -68
  19. package/lib/bridge/signOperation.js.map +1 -1
  20. package/lib/bridge/synchronization.js +32 -45
  21. package/lib/bridge/synchronization.js.map +1 -1
  22. package/lib/bridge/synchronization.test.js +21 -17
  23. package/lib/bridge/synchronization.test.js.map +1 -1
  24. package/lib/bridge/transaction.js +16 -4
  25. package/lib/bridge/transaction.js.map +1 -1
  26. package/lib/logic/broadcast.js +8 -19
  27. package/lib/logic/broadcast.js.map +1 -1
  28. package/lib/logic/broadcast.test.js +6 -15
  29. package/lib/logic/broadcast.test.js.map +1 -1
  30. package/lib/logic/combine.js +2 -2
  31. package/lib/logic/combine.js.map +1 -1
  32. package/lib/logic/craftTransaction.js +28 -34
  33. package/lib/logic/craftTransaction.js.map +1 -1
  34. package/lib/logic/craftTransaction.test.js +8 -17
  35. package/lib/logic/craftTransaction.test.js.map +1 -1
  36. package/lib/logic/estimateFees.js +17 -28
  37. package/lib/logic/estimateFees.js.map +1 -1
  38. package/lib/logic/estimateFees.test.js +10 -19
  39. package/lib/logic/estimateFees.test.js.map +1 -1
  40. package/lib/logic/getBalance.js +3 -14
  41. package/lib/logic/getBalance.js.map +1 -1
  42. package/lib/logic/getBalance.test.js +4 -13
  43. package/lib/logic/getBalance.test.js.map +1 -1
  44. package/lib/logic/lastBlock.js +7 -18
  45. package/lib/logic/lastBlock.js.map +1 -1
  46. package/lib/logic/listOperations.d.ts +15 -5
  47. package/lib/logic/listOperations.d.ts.map +1 -1
  48. package/lib/logic/listOperations.js +84 -71
  49. package/lib/logic/listOperations.js.map +1 -1
  50. package/lib/logic/listOperations.test.js +66 -62
  51. package/lib/logic/listOperations.test.js.map +1 -1
  52. package/lib/logic/utils.js +9 -18
  53. package/lib/logic/utils.js.map +1 -1
  54. package/lib/logic/utils.test.js +9 -18
  55. package/lib/logic/utils.test.js.map +1 -1
  56. package/lib/network/index.d.ts +6 -3
  57. package/lib/network/index.d.ts.map +1 -1
  58. package/lib/network/index.js +40 -48
  59. package/lib/network/index.js.map +1 -1
  60. package/lib/network/index.test.js +9 -18
  61. package/lib/network/index.test.js.map +1 -1
  62. package/lib/network/types.d.ts +5 -0
  63. package/lib/network/types.d.ts.map +1 -1
  64. package/lib/network/types.js.map +1 -1
  65. package/lib/signer/getAddress.js +3 -12
  66. package/lib/signer/getAddress.js.map +1 -1
  67. package/lib/test/cli.js +5 -1
  68. package/lib/test/cli.js.map +1 -1
  69. package/lib-es/api/index.d.ts.map +1 -1
  70. package/lib-es/api/index.integ.test.js +35 -36
  71. package/lib-es/api/index.integ.test.js.map +1 -1
  72. package/lib-es/api/index.js +65 -50
  73. package/lib-es/api/index.js.map +1 -1
  74. package/lib-es/api/index.test.js +66 -44
  75. package/lib-es/api/index.test.js.map +1 -1
  76. package/lib-es/bridge/broadcast.js +3 -12
  77. package/lib-es/bridge/broadcast.js.map +1 -1
  78. package/lib-es/bridge/estimateMaxSpendable.js +9 -14
  79. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
  80. package/lib-es/bridge/getTransactionStatus.js +4 -13
  81. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  82. package/lib-es/bridge/prepareTransaction.js +4 -13
  83. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  84. package/lib-es/bridge/signOperation.js +60 -68
  85. package/lib-es/bridge/signOperation.js.map +1 -1
  86. package/lib-es/bridge/synchronization.js +32 -45
  87. package/lib-es/bridge/synchronization.js.map +1 -1
  88. package/lib-es/bridge/synchronization.test.js +21 -17
  89. package/lib-es/bridge/synchronization.test.js.map +1 -1
  90. package/lib-es/bridge/transaction.js +16 -4
  91. package/lib-es/bridge/transaction.js.map +1 -1
  92. package/lib-es/logic/broadcast.js +8 -19
  93. package/lib-es/logic/broadcast.js.map +1 -1
  94. package/lib-es/logic/broadcast.test.js +6 -15
  95. package/lib-es/logic/broadcast.test.js.map +1 -1
  96. package/lib-es/logic/combine.js +2 -2
  97. package/lib-es/logic/combine.js.map +1 -1
  98. package/lib-es/logic/craftTransaction.js +28 -34
  99. package/lib-es/logic/craftTransaction.js.map +1 -1
  100. package/lib-es/logic/craftTransaction.test.js +8 -17
  101. package/lib-es/logic/craftTransaction.test.js.map +1 -1
  102. package/lib-es/logic/estimateFees.js +17 -28
  103. package/lib-es/logic/estimateFees.js.map +1 -1
  104. package/lib-es/logic/estimateFees.test.js +10 -19
  105. package/lib-es/logic/estimateFees.test.js.map +1 -1
  106. package/lib-es/logic/getBalance.js +3 -14
  107. package/lib-es/logic/getBalance.js.map +1 -1
  108. package/lib-es/logic/getBalance.test.js +4 -13
  109. package/lib-es/logic/getBalance.test.js.map +1 -1
  110. package/lib-es/logic/lastBlock.js +7 -18
  111. package/lib-es/logic/lastBlock.js.map +1 -1
  112. package/lib-es/logic/listOperations.d.ts +15 -5
  113. package/lib-es/logic/listOperations.d.ts.map +1 -1
  114. package/lib-es/logic/listOperations.js +84 -71
  115. package/lib-es/logic/listOperations.js.map +1 -1
  116. package/lib-es/logic/listOperations.test.js +66 -62
  117. package/lib-es/logic/listOperations.test.js.map +1 -1
  118. package/lib-es/logic/utils.js +9 -18
  119. package/lib-es/logic/utils.js.map +1 -1
  120. package/lib-es/logic/utils.test.js +9 -18
  121. package/lib-es/logic/utils.test.js.map +1 -1
  122. package/lib-es/network/index.d.ts +6 -3
  123. package/lib-es/network/index.d.ts.map +1 -1
  124. package/lib-es/network/index.js +39 -47
  125. package/lib-es/network/index.js.map +1 -1
  126. package/lib-es/network/index.test.js +9 -18
  127. package/lib-es/network/index.test.js.map +1 -1
  128. package/lib-es/network/types.d.ts +5 -0
  129. package/lib-es/network/types.d.ts.map +1 -1
  130. package/lib-es/network/types.js.map +1 -1
  131. package/lib-es/signer/getAddress.js +3 -12
  132. package/lib-es/signer/getAddress.js.map +1 -1
  133. package/lib-es/test/cli.js +5 -1
  134. package/lib-es/test/cli.js.map +1 -1
  135. package/package.json +7 -6
  136. package/src/api/index.integ.test.ts +12 -6
  137. package/src/api/index.test.ts +119 -75
  138. package/src/api/index.ts +62 -10
  139. package/src/bridge/synchronization.test.ts +31 -15
  140. package/src/logic/listOperations.test.ts +104 -85
  141. package/src/logic/listOperations.ts +47 -34
  142. package/src/network/index.ts +21 -8
  143. package/src/network/types.ts +6 -0
  144. package/tsconfig.json +0 -1
@@ -1,6 +1,7 @@
1
1
  import { assert } from "console";
2
2
  import { listOperations } from "./listOperations";
3
3
  import { RIPPLE_EPOCH } from "./utils";
4
+ import { Marker } from "../network/types";
4
5
 
5
6
  const maxHeight = 2;
6
7
  const minHeight = 1;
@@ -9,45 +10,60 @@ const mockGetServerInfos = jest.fn().mockResolvedValue({
9
10
  complete_ledgers: `${minHeight}-${maxHeight}`,
10
11
  },
11
12
  });
12
- const mockGetTransactions = jest.fn();
13
+ const mockNetworkGetTransactions = jest.fn();
13
14
  jest.mock("../network", () => ({
14
15
  getServerInfos: () => mockGetServerInfos(),
15
- getTransactions: () => mockGetTransactions(),
16
+ getTransactions: () => mockNetworkGetTransactions(),
16
17
  }));
17
18
 
18
19
  describe("listOperations", () => {
19
20
  afterEach(() => {
20
21
  mockGetServerInfos.mockClear();
21
- mockGetTransactions.mockClear();
22
+ mockNetworkGetTransactions.mockClear();
22
23
  });
23
24
 
25
+ const someMarker: Marker = { ledger: 1, seq: 1 };
26
+ function mockNetworkTxs(txs: unknown): unknown {
27
+ return {
28
+ account: "account",
29
+ ledger_index_max: 1,
30
+ ledger_index_min: 1,
31
+ limit: 1,
32
+ validated: false,
33
+ transactions: txs,
34
+ marker: someMarker,
35
+ error: "",
36
+ };
37
+ }
38
+
24
39
  it("when there are no transactions then the result is empty", async () => {
25
40
  // Given
26
- mockGetTransactions.mockResolvedValue([]);
41
+ mockNetworkGetTransactions.mockResolvedValue(mockNetworkTxs([]));
27
42
  // When
28
43
  const [results, token] = await listOperations("any address", { minHeight: 0 });
29
44
  // Then
30
45
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
31
- expect(mockGetTransactions).toHaveBeenCalledTimes(1);
46
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(1);
32
47
  expect(results).toEqual([]);
33
- expect(token).toEqual(minHeight);
48
+ expect(JSON.parse(token)).toEqual(someMarker);
34
49
  });
35
50
 
36
51
  it("when there are no transactions and a limit then the result is empty", async () => {
37
52
  // Given
38
- mockGetTransactions.mockResolvedValue([]);
53
+ mockNetworkGetTransactions.mockResolvedValue(mockNetworkTxs([]));
39
54
  // When
40
55
  const [results, token] = await listOperations("any address", { minHeight: 0, limit: 1 });
41
56
  // Then
42
57
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
43
- expect(mockGetTransactions).toHaveBeenCalledTimes(1);
58
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(1);
44
59
  expect(results).toEqual([]);
45
- expect(token).toEqual(minHeight);
60
+ expect(JSON.parse(token)).toEqual(someMarker);
46
61
  });
47
62
 
48
63
  const paymentTx = {
49
64
  ledger_hash: "HASH_VALUE_BLOCK",
50
65
  hash: "HASH_VALUE",
66
+ validated: true,
51
67
  close_time_iso: "2000-01-01T00:00:01Z",
52
68
  meta: { delivered_amount: "100" },
53
69
  tx_json: {
@@ -65,8 +81,9 @@ describe("listOperations", () => {
65
81
  it("should only list operations of type payment", async () => {
66
82
  // Given
67
83
  const lastTransaction = paymentTx;
68
- mockGetTransactions.mockResolvedValueOnce([paymentTx, otherTx, lastTransaction]);
69
- mockGetTransactions.mockResolvedValue([]); // subsequent calls
84
+ const txs = [paymentTx, otherTx, lastTransaction];
85
+ mockNetworkGetTransactions.mockResolvedValueOnce(mockNetworkTxs(txs));
86
+ mockNetworkGetTransactions.mockResolvedValue(mockNetworkTxs([])); // subsequent calls
70
87
 
71
88
  // When
72
89
  const [results, token] = await listOperations("any address", { minHeight: 0, limit: 3 });
@@ -74,39 +91,39 @@ describe("listOperations", () => {
74
91
  // Then
75
92
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
76
93
  // it's called twice because first call yields only 2 transactions, and 3 are asked
77
- expect(mockGetTransactions).toHaveBeenCalledTimes(2);
94
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(2);
78
95
  expect(results.length).toEqual(2);
79
- expect(token).toEqual(lastTransaction.tx_json.ledger_index - 1);
96
+ expect(JSON.parse(token)).toEqual(someMarker);
80
97
  });
81
98
 
82
99
  it("should make enough calls so that the limit requested is satified", async () => {
83
- const lastTransaction = otherTx;
84
100
  const txs = [paymentTx, paymentTx, otherTx, otherTx, otherTx, otherTx, otherTx, otherTx];
85
101
  assert(txs.length === 8);
86
- mockGetTransactions.mockResolvedValue(txs);
102
+ mockNetworkGetTransactions.mockResolvedValue(mockNetworkTxs(txs));
87
103
 
88
104
  const [results, token] = await listOperations("any address", { minHeight: 0, limit: 8 });
89
105
 
90
106
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
91
107
  // it's called 4 times because each call yields only 2 transactions, and 8 are asked
92
- expect(mockGetTransactions).toHaveBeenCalledTimes(4);
108
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(4);
93
109
  expect(results.length).toEqual(8);
94
- expect(token).toEqual(lastTransaction.tx_json.ledger_index - 1);
110
+ expect(JSON.parse(token)).toEqual(someMarker);
95
111
  });
96
112
 
97
113
  it("should make enough calls, even if there is not enough txs to satisfy the limit", async () => {
98
- mockGetTransactions.mockResolvedValueOnce([otherTx, otherTx, otherTx, otherTx]);
99
- mockGetTransactions.mockResolvedValueOnce([paymentTx, paymentTx]);
100
- mockGetTransactions.mockResolvedValue([]); // subsequent calls
101
- const lastTransaction = paymentTx;
114
+ mockNetworkGetTransactions.mockResolvedValueOnce(
115
+ mockNetworkTxs([otherTx, otherTx, otherTx, otherTx]),
116
+ );
117
+ mockNetworkGetTransactions.mockResolvedValueOnce(mockNetworkTxs([paymentTx, paymentTx]));
118
+ mockNetworkGetTransactions.mockResolvedValue([]); // subsequent calls
102
119
 
103
120
  const [results, token] = await listOperations("any address", { minHeight: 0, limit: 4 });
104
121
 
105
122
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
106
123
  // it's called 2 times because the second call is a shortage of txs
107
- expect(mockGetTransactions).toHaveBeenCalledTimes(2);
124
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(2);
108
125
  expect(results.length).toEqual(2);
109
- expect(token).toEqual(lastTransaction.tx_json.ledger_index - 1);
126
+ expect(JSON.parse(token)).toEqual(someMarker);
110
127
  });
111
128
 
112
129
  it.each([
@@ -128,69 +145,71 @@ describe("listOperations", () => {
128
145
  // Given
129
146
  const deliveredAmount = 100;
130
147
  const fee = 10;
131
- mockGetTransactions.mockResolvedValue([
132
- {
133
- ledger_hash: "HASH_VALUE_BLOCK",
134
- hash: "HASH_VALUE",
135
- close_time_iso: "2000-01-01T00:00:01Z",
136
- meta: { delivered_amount: deliveredAmount.toString() },
137
- tx_json: {
138
- TransactionType: "Payment",
139
- Fee: fee.toString(),
140
- ledger_index: 1,
141
- date: 1000,
142
- Account: opSender,
143
- Destination: opDestination,
144
- Sequence: 1,
148
+ mockNetworkGetTransactions.mockResolvedValue(
149
+ mockNetworkTxs([
150
+ {
151
+ ledger_hash: "HASH_VALUE_BLOCK",
152
+ hash: "HASH_VALUE",
153
+ close_time_iso: "2000-01-01T00:00:01Z",
154
+ meta: { delivered_amount: deliveredAmount.toString() },
155
+ tx_json: {
156
+ TransactionType: "Payment",
157
+ Fee: fee.toString(),
158
+ ledger_index: 1,
159
+ date: 1000,
160
+ Account: opSender,
161
+ Destination: opDestination,
162
+ Sequence: 1,
163
+ },
145
164
  },
146
- },
147
- {
148
- ledger_hash: "HASH_VALUE_BLOCK",
149
- hash: "HASH_VALUE",
150
- close_time_iso: "2000-01-01T00:00:01Z",
151
- meta: { delivered_amount: deliveredAmount.toString() },
152
- tx_json: {
153
- TransactionType: "Payment",
154
- Fee: fee.toString(),
155
- ledger_index: 1,
156
- date: 1000,
157
- Account: opSender,
158
- Destination: opDestination,
159
- DestinationTag: 509555,
160
- Sequence: 1,
165
+ {
166
+ ledger_hash: "HASH_VALUE_BLOCK",
167
+ hash: "HASH_VALUE",
168
+ close_time_iso: "2000-01-01T00:00:01Z",
169
+ meta: { delivered_amount: deliveredAmount.toString() },
170
+ tx_json: {
171
+ TransactionType: "Payment",
172
+ Fee: fee.toString(),
173
+ ledger_index: 1,
174
+ date: 1000,
175
+ Account: opSender,
176
+ Destination: opDestination,
177
+ DestinationTag: 509555,
178
+ Sequence: 1,
179
+ },
161
180
  },
162
- },
163
- {
164
- ledger_hash: "HASH_VALUE_BLOCK",
165
- hash: "HASH_VALUE",
166
- close_time_iso: "2000-01-01T00:00:01Z",
167
- meta: { delivered_amount: deliveredAmount.toString() },
168
- tx_json: {
169
- TransactionType: "Payment",
170
- Fee: fee.toString(),
171
- ledger_index: 1,
172
- date: 1000,
173
- Account: opSender,
174
- Destination: opDestination,
175
- Memos: [
176
- {
177
- Memo: {
178
- MemoType: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
179
- MemoData: "72656e74",
181
+ {
182
+ ledger_hash: "HASH_VALUE_BLOCK",
183
+ hash: "HASH_VALUE",
184
+ close_time_iso: "2000-01-01T00:00:01Z",
185
+ meta: { delivered_amount: deliveredAmount.toString() },
186
+ tx_json: {
187
+ TransactionType: "Payment",
188
+ Fee: fee.toString(),
189
+ ledger_index: 1,
190
+ date: 1000,
191
+ Account: opSender,
192
+ Destination: opDestination,
193
+ Memos: [
194
+ {
195
+ Memo: {
196
+ MemoType: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
197
+ MemoData: "72656e74",
198
+ },
180
199
  },
181
- },
182
- ],
183
- Sequence: 1,
200
+ ],
201
+ Sequence: 1,
202
+ },
184
203
  },
185
- },
186
- ]);
204
+ ]),
205
+ );
187
206
 
188
207
  // When
189
- const [results, _] = await listOperations(address, { minHeight: 0 });
208
+ const [results, _] = await listOperations(address, { minHeight: 0, order: "asc" });
190
209
 
191
210
  // Then
192
211
  expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
193
- expect(mockGetTransactions).toHaveBeenCalledTimes(1);
212
+ expect(mockNetworkGetTransactions).toHaveBeenCalledTimes(1);
194
213
  // if expectedType is "OUT", compute value with fees (i.e. delivered_amount + Fee)
195
214
  const expectedValue =
196
215
  expectedType === "IN" ? BigInt(deliveredAmount) : BigInt(deliveredAmount + fee);
@@ -209,6 +228,14 @@ describe("listOperations", () => {
209
228
  recipients: [opDestination],
210
229
  date: new Date(1000000 + RIPPLE_EPOCH * 1000),
211
230
  transactionSequenceNumber: 1,
231
+ details: {
232
+ memos: [
233
+ {
234
+ type: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
235
+ data: "72656e74",
236
+ },
237
+ ],
238
+ },
212
239
  },
213
240
  {
214
241
  hash: "HASH_VALUE",
@@ -242,14 +269,6 @@ describe("listOperations", () => {
242
269
  recipients: [opDestination],
243
270
  date: new Date(1000000 + RIPPLE_EPOCH * 1000),
244
271
  transactionSequenceNumber: 1,
245
- details: {
246
- memos: [
247
- {
248
- type: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
249
- data: "72656e74",
250
- },
251
- ],
252
- },
253
272
  },
254
273
  ]);
255
274
  },
@@ -1,41 +1,50 @@
1
- import { getServerInfos, getTransactions } from "../network";
1
+ import { getServerInfos, getTransactions, GetTransactionsOptions } from "../network";
2
2
  import type { XrplOperation } from "../network/types";
3
3
  import { XrpMemo, XrpOperation } from "../types";
4
4
  import { RIPPLE_EPOCH } from "./utils";
5
5
 
6
+ type Order = "asc" | "desc";
6
7
  /**
7
8
  * Returns list of "Payment" Operations associated to an account.
8
9
  * @param address Account address
9
- * @param blockHeight Height to start searching for operations
10
- * @returns
10
+ * @param minHeight retrieve operations from a specific block height until top most (inclusive)
11
+ * if not provided, it will start from the oldest possible history.
12
+ * The result is not guaranteed to contain all operations until top height (it depends of the underlying explorer),
13
+ * so you might need to call this function multiple times to get all operations.
14
+ * @param order whether to return operations from the top block or from the oldest block
15
+ * it defaults to "desc" (newest first)
16
+ * it doesn't control the order of the operations in the result list.
17
+ * this parameter is added as a workaround for the issue LIVE-16705
18
+ * @returns a list of operations is descending order and a token to be used for pagination
11
19
  */
12
20
  export async function listOperations(
13
21
  address: string,
14
22
  {
15
23
  limit,
16
- maxHeight,
17
24
  minHeight,
25
+ token,
26
+ order,
18
27
  }: {
28
+ // pagination:
19
29
  limit?: number;
20
- maxHeight?: number | undefined; // used for pagination
21
- minHeight?: number; // used to retrieve operations from a specific block height until top most
30
+ token?: string;
31
+ order?: Order;
32
+ // filters:
33
+ minHeight?: number;
22
34
  },
23
- ): Promise<[XrpOperation[], number]> {
35
+ ): Promise<[XrpOperation[], string]> {
24
36
  const serverInfo = await getServerInfos();
25
37
  const ledgers = serverInfo.info.complete_ledgers.split("-");
26
38
  const minLedgerVersion = Number(ledgers[0]);
27
- const maxLedgerVersion = Number(ledgers[1]);
28
39
 
29
- type Options = {
30
- ledger_index_min?: number;
31
- ledger_index_max?: number;
32
- limit?: number;
33
- tx_type?: string;
34
- };
40
+ // by default the explorer queries the transactions in descending order (newest first)
41
+ let forward = false;
42
+ if (order && order === "asc") {
43
+ forward = true;
44
+ }
35
45
 
36
- let options: Options = {
37
- ledger_index_max: maxHeight ?? maxLedgerVersion,
38
- tx_type: "Payment",
46
+ let options: GetTransactionsOptions = {
47
+ forward: forward,
39
48
  };
40
49
 
41
50
  if (limit) {
@@ -44,7 +53,15 @@ export async function listOperations(
44
53
  limit,
45
54
  };
46
55
  }
47
- if (minHeight) {
56
+
57
+ if (token) {
58
+ options = {
59
+ ...options,
60
+ marker: JSON.parse(token),
61
+ };
62
+ }
63
+
64
+ if (minHeight !== undefined) {
48
65
  options = {
49
66
  ...options,
50
67
  // if there is no ops, it might be after a clear and we prefer to pull from the oldest possible history
@@ -54,25 +71,22 @@ export async function listOperations(
54
71
 
55
72
  async function getPaymentTransactions(
56
73
  address: string,
57
- options: Options,
58
- ): Promise<[boolean, Options, XrplOperation[]]> {
59
- const txs = await getTransactions(address, options);
74
+ options: GetTransactionsOptions,
75
+ ): Promise<[boolean, GetTransactionsOptions, XrplOperation[]]> {
76
+ const response = await getTransactions(address, options);
77
+ const txs = response.transactions;
78
+ const marker = response.marker;
60
79
  // Filter out the transactions that are not "Payment" type because the filter on "tx_type" of the node RPC is not working as expected.
61
80
  const paymentTxs = txs.filter(tx => tx.tx_json.TransactionType === "Payment");
62
81
  const shortage = (options.limit && txs.length < options.limit) || false;
63
- const lastTransaction = txs.slice(-1)[0];
64
82
  const nextOptions = { ...options };
65
- if (lastTransaction) {
66
- nextOptions.ledger_index_max = lastTransaction.tx_json.ledger_index - 1;
83
+ if (marker) {
84
+ nextOptions.marker = marker;
67
85
  if (nextOptions.limit) nextOptions.limit -= paymentTxs.length;
68
86
  }
69
87
  return [shortage, nextOptions, paymentTxs];
70
88
  }
71
89
 
72
- // TODO BUG: given the number of txs belonging to the SAME block > limit
73
- // when user loop over pages using the provided token
74
- // then user misses some txs that doesn't fit the page size limit
75
- // because the "next token" is a block height (solution is to use an opaque token instead)
76
90
  let [txShortage, nextOptions, transactions] = await getPaymentTransactions(address, options);
77
91
  const isEnough = () => txShortage || (limit && transactions.length >= limit);
78
92
  // We need to call the node RPC multiple times to get the desired number of transactions by the limiter.
@@ -86,13 +100,12 @@ export async function listOperations(
86
100
  transactions = transactions.concat(newTransactions);
87
101
  }
88
102
 
89
- const lastTransaction = transactions.slice(-1)[0];
90
- // the next index to start the pagination from
91
- const nextIndex = lastTransaction
92
- ? Math.max(lastTransaction.tx_json.ledger_index - 1, minLedgerVersion)
93
- : minLedgerVersion;
103
+ // the order is reversed so that the results are always sorted by newest tx first element of the list
104
+ if (order === "asc") transactions.reverse();
94
105
 
95
- return [transactions.map(convertToCoreOperation(address)), nextIndex];
106
+ // the next index to start the pagination from
107
+ const next = nextOptions.marker ? JSON.stringify(nextOptions.marker) : "";
108
+ return [transactions.map(convertToCoreOperation(address)), next];
96
109
  }
97
110
 
98
111
  const convertToCoreOperation =
@@ -4,13 +4,13 @@ import type { AccountInfo } from "../types/model";
4
4
  import {
5
5
  isErrorResponse,
6
6
  isResponseStatus,
7
+ Marker,
7
8
  type AccountInfoResponse,
8
9
  type AccountTxResponse,
9
10
  type ErrorResponse,
10
11
  type LedgerResponse,
11
12
  type ServerInfoResponse,
12
13
  type SubmitReponse,
13
- type XrplOperation,
14
14
  } from "./types";
15
15
 
16
16
  const getNodeUrl = () => coinConfig.getCoinConfig().node;
@@ -66,19 +66,32 @@ export const getServerInfos = async (): Promise<ServerInfoResponse> => {
66
66
  return rpcCall<ServerInfoResponse>("server_info", { ledger_index: "validated" });
67
67
  };
68
68
 
69
+ // https://xrpl.org/docs/references/http-websocket-apis/public-api-methods/account-methods/account_tx
70
+ export type GetTransactionsOptions = {
71
+ ledger_index_min?: number;
72
+ ledger_index_max?: number;
73
+ limit?: number;
74
+ marker?: Marker;
75
+ // this property controls the order of the transactions
76
+ // true: oldest first
77
+ // false: newest first
78
+ forward: boolean;
79
+ };
80
+
69
81
  export const getTransactions = async (
70
82
  address: string,
71
- options: { ledger_index_min?: number; ledger_index_max?: number; limit?: number } | undefined,
72
- ): Promise<XrplOperation[]> => {
83
+ options: GetTransactionsOptions | undefined,
84
+ ): Promise<AccountTxResponse> => {
73
85
  const result = await rpcCall<AccountTxResponse>("account_tx", {
74
86
  account: address,
75
- // newest first
76
- // note that order within the results is not guaranteed (see documentation of account_tx)
77
- forward: false,
87
+ // this property controls the order of the transactions
88
+ // looks like there is a bug in LL (https://ledgerhq.atlassian.net/browse/LIVE-16705)
89
+ // so we need to set it to false (newest first) to get the transactions in the right order
90
+ // for lama-adapter we need to set it to true (oldest first)
78
91
  ...options,
79
92
  api_version: 2,
80
93
  });
81
- return result.transactions;
94
+ return result;
82
95
  };
83
96
 
84
97
  export async function getLedger(): Promise<LedgerResponse> {
@@ -93,7 +106,7 @@ export async function getLedgerIndex(): Promise<number> {
93
106
 
94
107
  async function rpcCall<T extends object>(
95
108
  method: string,
96
- params: Record<string, string | number | boolean> = {},
109
+ params: Record<string, unknown> = {},
97
110
  ): Promise<T> {
98
111
  const {
99
112
  data: { result },
@@ -168,6 +168,11 @@ export type ServerInfoResponse = {
168
168
  };
169
169
  } & ResponseStatus;
170
170
 
171
+ export type Marker = {
172
+ ledger: number;
173
+ seq: number;
174
+ };
175
+
171
176
  export type AccountTxResponse = {
172
177
  account: string;
173
178
  ledger_index_max: number;
@@ -175,6 +180,7 @@ export type AccountTxResponse = {
175
180
  limit: number;
176
181
  transactions: XrplOperation[];
177
182
  validated: boolean;
183
+ marker?: Marker;
178
184
  } & ResponseStatus;
179
185
 
180
186
  export type LedgerResponse = {
package/tsconfig.json CHANGED
@@ -3,7 +3,6 @@
3
3
  "compilerOptions": {
4
4
  "declaration": true,
5
5
  "declarationMap": true,
6
- "module": "commonjs",
7
6
  "downlevelIteration": true,
8
7
  "lib": ["es2020", "dom"],
9
8
  "outDir": "lib",