@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
@@ -20,6 +20,98 @@ describe("listOperations", () => {
20
20
  mockGetTransactions.mockClear();
21
21
  });
22
22
 
23
+ const defaultMarker = { ledger: 1, seq: 1 };
24
+ function mockNetworkTxs(txs: unknown, marker: undefined | unknown): unknown {
25
+ return {
26
+ account: "account",
27
+ ledger_index_max: 1,
28
+ ledger_index_min: 1,
29
+ limit: 1,
30
+ validated: false,
31
+ transactions: txs,
32
+ marker: marker,
33
+ error: "",
34
+ };
35
+ }
36
+
37
+ function givenTxs(
38
+ fee: bigint,
39
+ deliveredAmount: bigint,
40
+ opSender: string,
41
+ opDestination: string,
42
+ ): unknown[] {
43
+ return [
44
+ {
45
+ ledger_hash: "HASH_VALUE_BLOCK",
46
+ hash: "HASH_VALUE",
47
+ close_time_iso: "2000-01-01T00:00:01Z",
48
+ meta: { delivered_amount: deliveredAmount.toString() },
49
+ tx_json: {
50
+ TransactionType: "Payment",
51
+ Fee: fee.toString(),
52
+ ledger_index: 1,
53
+ date: 1000,
54
+ Account: opSender,
55
+ Destination: opDestination,
56
+ Sequence: 1,
57
+ },
58
+ },
59
+ {
60
+ ledger_hash: "HASH_VALUE_BLOCK",
61
+ hash: "HASH_VALUE",
62
+ close_time_iso: "2000-01-01T00:00:01Z",
63
+ meta: { delivered_amount: deliveredAmount.toString() },
64
+ tx_json: {
65
+ TransactionType: "Payment",
66
+ Fee: fee.toString(),
67
+ ledger_index: 1,
68
+ date: 1000,
69
+ Account: opSender,
70
+ Destination: opDestination,
71
+ DestinationTag: 509555,
72
+ Sequence: 1,
73
+ },
74
+ },
75
+ {
76
+ ledger_hash: "HASH_VALUE_BLOCK",
77
+ hash: "HASH_VALUE",
78
+ close_time_iso: "2000-01-01T00:00:01Z",
79
+ meta: { delivered_amount: deliveredAmount.toString() },
80
+ tx_json: {
81
+ TransactionType: "Payment",
82
+ Fee: fee.toString(),
83
+ ledger_index: 1,
84
+ date: 1000,
85
+ Account: opSender,
86
+ Destination: opDestination,
87
+ Memos: [
88
+ {
89
+ Memo: {
90
+ MemoType: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
91
+ MemoData: "72656e74",
92
+ },
93
+ },
94
+ ],
95
+ Sequence: 1,
96
+ },
97
+ },
98
+ ];
99
+ }
100
+
101
+ it("should kill the loop after 10 iterations", async () => {
102
+ const txs = givenTxs(BigInt(10), BigInt(10), "src", "dest");
103
+ // each time it's called it returns a marker, so in theory it would loop forever
104
+ mockGetTransactions.mockResolvedValue(mockNetworkTxs(txs, defaultMarker));
105
+ const [results, _] = await api.listOperations("src", { minHeight: 0 });
106
+
107
+ // called 10 times because there is a hard limit of 10 iterations in case something goes wrong
108
+ // with interpretation of the token (bug / explorer api changed ...)
109
+ expect(mockGetServerInfos).toHaveBeenCalledTimes(10);
110
+ expect(mockGetTransactions).toHaveBeenCalledTimes(10);
111
+
112
+ expect(results.length).toBe(txs.length * 10);
113
+ });
114
+
23
115
  it.each([
24
116
  {
25
117
  address: "WHATEVER_ADDRESS",
@@ -34,84 +126,36 @@ describe("listOperations", () => {
34
126
  expectedType: "OUT",
35
127
  },
36
128
  ])(
37
- "should return the list of operations associated to an account",
129
+ `should return the list of operations associated to an account when expected type is %s`,
38
130
  async ({ address, opSender, opDestination, expectedType }) => {
39
131
  // Givem
40
- const deliveredAmount = 100;
41
- const fee = 10;
42
- mockGetTransactions.mockResolvedValue([
43
- {
44
- ledger_hash: "HASH_VALUE_BLOCK",
45
- hash: "HASH_VALUE",
46
- close_time_iso: "2000-01-01T00:00:01Z",
47
- meta: { delivered_amount: deliveredAmount.toString() },
48
- tx_json: {
49
- TransactionType: "Payment",
50
- Fee: fee.toString(),
51
- ledger_index: 1,
52
- date: 1000,
53
- Account: opSender,
54
- Destination: opDestination,
55
- Sequence: 1,
56
- },
57
- },
58
- {
59
- ledger_hash: "HASH_VALUE_BLOCK",
60
- hash: "HASH_VALUE",
61
- close_time_iso: "2000-01-01T00:00:01Z",
62
- meta: { delivered_amount: deliveredAmount.toString() },
63
- tx_json: {
64
- TransactionType: "Payment",
65
- Fee: fee.toString(),
66
- ledger_index: 1,
67
- date: 1000,
68
- Account: opSender,
69
- Destination: opDestination,
70
- DestinationTag: 509555,
71
- Sequence: 1,
72
- },
73
- },
74
- {
75
- ledger_hash: "HASH_VALUE_BLOCK",
76
- hash: "HASH_VALUE",
77
- close_time_iso: "2000-01-01T00:00:01Z",
78
- meta: { delivered_amount: deliveredAmount.toString() },
79
- tx_json: {
80
- TransactionType: "Payment",
81
- Fee: fee.toString(),
82
- ledger_index: 1,
83
- date: 1000,
84
- Account: opSender,
85
- Destination: opDestination,
86
- Memos: [
87
- {
88
- Memo: {
89
- MemoType: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
90
- MemoData: "72656e74",
91
- },
92
- },
93
- ],
94
- Sequence: 1,
95
- },
96
- },
97
- ]);
132
+ const deliveredAmount = BigInt(100);
133
+ const fee = BigInt(10);
134
+ mockGetTransactions.mockResolvedValueOnce(
135
+ mockNetworkTxs(givenTxs(fee, deliveredAmount, opSender, opDestination), defaultMarker),
136
+ );
137
+
138
+ // second call to kill the loop
139
+ mockGetTransactions.mockResolvedValue(mockNetworkTxs([], undefined));
98
140
 
99
141
  // When
100
- const [results, _] = await api.listOperations(address, { limit: 100 });
142
+ const [results, _] = await api.listOperations(address, { minHeight: 0 });
101
143
 
102
144
  // Then
103
- expect(mockGetServerInfos).toHaveBeenCalledTimes(1);
104
- expect(mockGetTransactions).toHaveBeenCalledTimes(1);
145
+ // called twice because the marker is set the first time
146
+ expect(mockGetServerInfos).toHaveBeenCalledTimes(2);
147
+ expect(mockGetTransactions).toHaveBeenCalledTimes(2);
148
+
105
149
  // if expectedType is "OUT", compute value with fees (i.e. delivered_amount + Fee)
106
- const expectedValue =
107
- expectedType === "IN" ? BigInt(deliveredAmount) : BigInt(deliveredAmount + fee);
150
+ const expectedValue = expectedType === "IN" ? deliveredAmount : deliveredAmount + fee;
151
+ // the order is reversed so that the result is always sorted by newest tx first element of the list
108
152
  expect(results).toEqual([
109
153
  {
110
154
  hash: "HASH_VALUE",
111
155
  address,
112
156
  type: "Payment",
113
157
  value: expectedValue,
114
- fee: BigInt(10),
158
+ fee: fee,
115
159
  block: {
116
160
  hash: "HASH_VALUE_BLOCK",
117
161
  height: 1,
@@ -121,13 +165,21 @@ describe("listOperations", () => {
121
165
  recipients: [opDestination],
122
166
  date: new Date(1000000 + RIPPLE_EPOCH * 1000),
123
167
  transactionSequenceNumber: 1,
168
+ details: {
169
+ memos: [
170
+ {
171
+ type: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
172
+ data: "72656e74",
173
+ },
174
+ ],
175
+ },
124
176
  },
125
177
  {
126
178
  hash: "HASH_VALUE",
127
179
  address,
128
180
  type: "Payment",
129
181
  value: expectedValue,
130
- fee: BigInt(10),
182
+ fee: fee,
131
183
  block: {
132
184
  hash: "HASH_VALUE_BLOCK",
133
185
  height: 1,
@@ -146,7 +198,7 @@ describe("listOperations", () => {
146
198
  address,
147
199
  type: "Payment",
148
200
  value: expectedValue,
149
- fee: BigInt(10),
201
+ fee: fee,
150
202
  block: {
151
203
  hash: "HASH_VALUE_BLOCK",
152
204
  height: 1,
@@ -156,14 +208,6 @@ describe("listOperations", () => {
156
208
  recipients: [opDestination],
157
209
  date: new Date(1000000 + RIPPLE_EPOCH * 1000),
158
210
  transactionSequenceNumber: 1,
159
- details: {
160
- memos: [
161
- {
162
- type: "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
163
- data: "72656e74",
164
- },
165
- ],
166
- },
167
211
  },
168
212
  ]);
169
213
  },
package/src/api/index.ts CHANGED
@@ -4,6 +4,7 @@ import type {
4
4
  Transaction as ApiTransaction,
5
5
  Pagination,
6
6
  } from "@ledgerhq/coin-framework/api/index";
7
+ import { log } from "@ledgerhq/logs";
7
8
  import coinConfig, { type XrpConfig } from "../config";
8
9
  import {
9
10
  broadcast,
@@ -15,6 +16,7 @@ import {
15
16
  lastBlock,
16
17
  listOperations,
17
18
  } from "../logic";
19
+ import { XrpOperation } from "../types";
18
20
 
19
21
  export function createApi(config: XrpConfig): Api {
20
22
  coinConfig.setCoinConfig(() => ({ ...config, status: { type: "active" } }));
@@ -41,16 +43,66 @@ async function estimate(_addr: string, _amount: bigint): Promise<bigint> {
41
43
  return fees.fee;
42
44
  }
43
45
 
46
+ type PaginationState = {
47
+ pageSize: number; // must be large enough to avoid unnecessary calls to the underlying explorer
48
+ maxIterations: number; // a security to avoid infinite loop
49
+ currentIteration: number;
50
+ minHeight: number;
51
+ continueIterations: boolean;
52
+ apiNextCursor?: string;
53
+ accumulator: XrpOperation[];
54
+ };
55
+
56
+ async function operationsFromHeight(
57
+ address: string,
58
+ minHeight: number,
59
+ ): Promise<[XrpOperation[], string]> {
60
+ async function fetchNextPage(state: PaginationState): Promise<PaginationState> {
61
+ const [operations, apiNextCursor] = await listOperations(address, {
62
+ limit: state.pageSize,
63
+ minHeight: state.minHeight,
64
+ order: "asc",
65
+ });
66
+ const newCurrentIteration = state.currentIteration + 1;
67
+ let continueIteration = true;
68
+ if (apiNextCursor === "") {
69
+ continueIteration = false;
70
+ } else if (newCurrentIteration >= state.maxIterations) {
71
+ log("coin:xrp", "(api/operations): max iterations reached", state.maxIterations);
72
+ continueIteration = false;
73
+ }
74
+ const accumulated = state.accumulator.concat(operations);
75
+ return {
76
+ ...state,
77
+ currentIteration: newCurrentIteration,
78
+ continueIterations: continueIteration,
79
+ apiNextCursor: apiNextCursor,
80
+ accumulator: accumulated,
81
+ };
82
+ }
83
+
84
+ const firstState: PaginationState = {
85
+ pageSize: 200,
86
+ maxIterations: 10,
87
+ currentIteration: 0,
88
+ minHeight: minHeight,
89
+ continueIterations: true,
90
+ accumulator: [],
91
+ };
92
+
93
+ let state = await fetchNextPage(firstState);
94
+ while (state.continueIterations) {
95
+ state = await fetchNextPage(state);
96
+ }
97
+ return [state.accumulator, state.apiNextCursor ?? ""];
98
+ }
99
+
44
100
  async function operations(
45
101
  address: string,
46
- { limit, start }: Pagination,
47
- ): Promise<[Operation[], number]> {
48
- const options: {
49
- limit?: number;
50
- minHeight?: number;
51
- } = { limit: limit };
52
- if (start) options.minHeight = start;
53
- const [ops, index] = await listOperations(address, options);
102
+ { minHeight }: Pagination,
103
+ ): Promise<[Operation[], string]> {
104
+ const [ops, token] = await operationsFromHeight(address, minHeight);
105
+ // TODO token must be implemented properly (waiting ack from the design document)
54
106
  return [
55
107
  ops.map(op => {
56
108
  const { simpleType, blockHash, blockTime, blockHeight, ...rest } = op;
@@ -61,8 +113,8 @@ async function operations(
61
113
  hash: blockHash,
62
114
  time: blockTime,
63
115
  },
64
- } satisfies Operation;
116
+ };
65
117
  }),
66
- index,
118
+ token,
67
119
  ];
68
120
  }
@@ -68,6 +68,20 @@ describe("getAccountShape", () => {
68
68
  });
69
69
  });
70
70
 
71
+ const someMarker = { ledger: 1, seq: 1 };
72
+ function mockNetworkTxs(txs: unknown): unknown {
73
+ return {
74
+ account: "account",
75
+ ledger_index_max: 1,
76
+ ledger_index_min: 1,
77
+ limit: 1,
78
+ validated: false,
79
+ transactions: txs,
80
+ marker: someMarker,
81
+ error: "",
82
+ };
83
+ }
84
+
71
85
  it("convert correctly operations", async () => {
72
86
  // Given
73
87
  mockGetAccountInfo.mockResolvedValue({
@@ -76,22 +90,24 @@ describe("getAccountShape", () => {
76
90
  ownerCount: 0,
77
91
  sequence: 0,
78
92
  });
79
- mockGetTransactions.mockResolvedValue([
80
- {
81
- ledger_hash: "HASH_VALUE_BLOCK",
82
- hash: "HASH_VALUE",
83
- meta: { delivered_amount: "100" },
84
- tx_json: {
85
- TransactionType: "Payment",
86
- Fee: "10",
87
- ledger_index: 1,
88
- date: 1000,
89
- Account: "account_addr",
90
- Destination: "destination_addr",
91
- Sequence: 1,
93
+ mockGetTransactions.mockResolvedValue(
94
+ mockNetworkTxs([
95
+ {
96
+ ledger_hash: "HASH_VALUE_BLOCK",
97
+ hash: "HASH_VALUE",
98
+ meta: { delivered_amount: "100" },
99
+ tx_json: {
100
+ TransactionType: "Payment",
101
+ Fee: "10",
102
+ ledger_index: 1,
103
+ date: 1000,
104
+ Account: "account_addr",
105
+ Destination: "destination_addr",
106
+ Sequence: 1,
107
+ },
92
108
  },
93
- },
94
- ]);
109
+ ]),
110
+ );
95
111
 
96
112
  // When
97
113
  const account = await getAccountShape(