@ledgerhq/coin-stellar 6.26.0 → 6.26.2

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 (281) hide show
  1. package/.oxlintrc.json +7 -0
  2. package/CHANGELOG.md +12 -0
  3. package/LICENSE +194 -0
  4. package/knip.json +12 -0
  5. package/lib/api/index.d.ts +5 -5
  6. package/lib/api/index.d.ts.map +1 -1
  7. package/lib/api/index.js +12 -10
  8. package/lib/api/index.js.map +1 -1
  9. package/lib/config.d.ts +1 -1
  10. package/lib/config.d.ts.map +1 -1
  11. package/lib/config.js +2 -0
  12. package/lib/config.js.map +1 -1
  13. package/lib/constants.d.ts.map +1 -1
  14. package/lib/constants.js +3 -1
  15. package/lib/constants.js.map +1 -1
  16. package/lib/logic/broadcast.d.ts.map +1 -1
  17. package/lib/logic/broadcast.js +2 -0
  18. package/lib/logic/broadcast.js.map +1 -1
  19. package/lib/logic/combine.d.ts +1 -1
  20. package/lib/logic/combine.d.ts.map +1 -1
  21. package/lib/logic/combine.js +2 -0
  22. package/lib/logic/combine.js.map +1 -1
  23. package/lib/logic/common.d.ts +1 -1
  24. package/lib/logic/common.d.ts.map +1 -1
  25. package/lib/logic/common.js +4 -2
  26. package/lib/logic/common.js.map +1 -1
  27. package/lib/logic/craftTransaction.d.ts +1 -1
  28. package/lib/logic/craftTransaction.d.ts.map +1 -1
  29. package/lib/logic/craftTransaction.js +10 -8
  30. package/lib/logic/craftTransaction.js.map +1 -1
  31. package/lib/logic/estimateFees.d.ts.map +1 -1
  32. package/lib/logic/estimateFees.js +2 -0
  33. package/lib/logic/estimateFees.js.map +1 -1
  34. package/lib/logic/getBalance.d.ts +1 -1
  35. package/lib/logic/getBalance.d.ts.map +1 -1
  36. package/lib/logic/getBalance.js +4 -2
  37. package/lib/logic/getBalance.js.map +1 -1
  38. package/lib/logic/getBlock.d.ts +1 -1
  39. package/lib/logic/getBlock.d.ts.map +1 -1
  40. package/lib/logic/getBlock.js +39 -34
  41. package/lib/logic/getBlock.js.map +1 -1
  42. package/lib/logic/getBlockInfo.d.ts +1 -1
  43. package/lib/logic/getBlockInfo.d.ts.map +1 -1
  44. package/lib/logic/getBlockInfo.js +2 -0
  45. package/lib/logic/getBlockInfo.js.map +1 -1
  46. package/lib/logic/index.d.ts +12 -12
  47. package/lib/logic/index.d.ts.map +1 -1
  48. package/lib/logic/index.js +2 -0
  49. package/lib/logic/index.js.map +1 -1
  50. package/lib/logic/lastBlock.d.ts +1 -1
  51. package/lib/logic/lastBlock.d.ts.map +1 -1
  52. package/lib/logic/lastBlock.js +2 -0
  53. package/lib/logic/lastBlock.js.map +1 -1
  54. package/lib/logic/listOperations.d.ts +1 -1
  55. package/lib/logic/listOperations.d.ts.map +1 -1
  56. package/lib/logic/listOperations.js +6 -4
  57. package/lib/logic/listOperations.js.map +1 -1
  58. package/lib/logic/sdkWrapper.d.ts +1 -1
  59. package/lib/logic/sdkWrapper.d.ts.map +1 -1
  60. package/lib/logic/sdkWrapper.js +2 -0
  61. package/lib/logic/sdkWrapper.js.map +1 -1
  62. package/lib/logic/utils.d.ts.map +1 -1
  63. package/lib/logic/utils.js +4 -2
  64. package/lib/logic/utils.js.map +1 -1
  65. package/lib/logic/validateAddress.d.ts +1 -1
  66. package/lib/logic/validateAddress.d.ts.map +1 -1
  67. package/lib/logic/validateAddress.js +2 -0
  68. package/lib/logic/validateAddress.js.map +1 -1
  69. package/lib/logic/validateIntent.d.ts +2 -2
  70. package/lib/logic/validateIntent.d.ts.map +1 -1
  71. package/lib/logic/validateIntent.js +21 -19
  72. package/lib/logic/validateIntent.js.map +1 -1
  73. package/lib/logic/validateMemo.d.ts +1 -1
  74. package/lib/logic/validateMemo.d.ts.map +1 -1
  75. package/lib/logic/validateMemo.js +7 -5
  76. package/lib/logic/validateMemo.js.map +1 -1
  77. package/lib/network/horizon.d.ts +6 -7
  78. package/lib/network/horizon.d.ts.map +1 -1
  79. package/lib/network/horizon.js +48 -95
  80. package/lib/network/horizon.js.map +1 -1
  81. package/lib/network/horizonErrorBody.d.ts +29 -0
  82. package/lib/network/horizonErrorBody.d.ts.map +1 -0
  83. package/lib/network/horizonErrorBody.js +139 -0
  84. package/lib/network/horizonErrorBody.js.map +1 -0
  85. package/lib/network/horizonLedgerErrors.d.ts.map +1 -1
  86. package/lib/network/horizonLedgerErrors.js +28 -4
  87. package/lib/network/horizonLedgerErrors.js.map +1 -1
  88. package/lib/network/index.d.ts +1 -1
  89. package/lib/network/index.d.ts.map +1 -1
  90. package/lib/network/index.js +2 -0
  91. package/lib/network/index.js.map +1 -1
  92. package/lib/network/serialization.d.ts +6 -4
  93. package/lib/network/serialization.d.ts.map +1 -1
  94. package/lib/network/serialization.js +95 -91
  95. package/lib/network/serialization.js.map +1 -1
  96. package/lib/polyfill.d.ts.map +1 -1
  97. package/lib/polyfill.js +6 -4
  98. package/lib/polyfill.js.map +1 -1
  99. package/lib/supportedFeatures.d.ts +1 -1
  100. package/lib/supportedFeatures.d.ts.map +1 -1
  101. package/lib/supportedFeatures.js +3 -1
  102. package/lib/supportedFeatures.js.map +1 -1
  103. package/lib/types/errors.d.ts.map +1 -1
  104. package/lib/types/errors.js +14 -12
  105. package/lib/types/errors.js.map +1 -1
  106. package/lib/types/index.d.ts +2 -2
  107. package/lib/types/index.d.ts.map +1 -1
  108. package/lib/types/index.js +2 -0
  109. package/lib/types/index.js.map +1 -1
  110. package/lib/types/model.d.ts +6 -6
  111. package/lib/types/model.d.ts.map +1 -1
  112. package/lib/types/model.js +7 -5
  113. package/lib/types/model.js.map +1 -1
  114. package/lib-es/api/index.d.ts +5 -5
  115. package/lib-es/api/index.d.ts.map +1 -1
  116. package/lib-es/api/index.js +20 -18
  117. package/lib-es/api/index.js.map +1 -1
  118. package/lib-es/config.d.ts +1 -1
  119. package/lib-es/config.d.ts.map +1 -1
  120. package/lib-es/config.js +3 -1
  121. package/lib-es/config.js.map +1 -1
  122. package/lib-es/constants.d.ts.map +1 -1
  123. package/lib-es/constants.js +3 -1
  124. package/lib-es/constants.js.map +1 -1
  125. package/lib-es/logic/broadcast.d.ts.map +1 -1
  126. package/lib-es/logic/broadcast.js +3 -1
  127. package/lib-es/logic/broadcast.js.map +1 -1
  128. package/lib-es/logic/combine.d.ts +1 -1
  129. package/lib-es/logic/combine.d.ts.map +1 -1
  130. package/lib-es/logic/combine.js +3 -1
  131. package/lib-es/logic/combine.js.map +1 -1
  132. package/lib-es/logic/common.d.ts +1 -1
  133. package/lib-es/logic/common.d.ts.map +1 -1
  134. package/lib-es/logic/common.js +5 -3
  135. package/lib-es/logic/common.js.map +1 -1
  136. package/lib-es/logic/craftTransaction.d.ts +1 -1
  137. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  138. package/lib-es/logic/craftTransaction.js +15 -13
  139. package/lib-es/logic/craftTransaction.js.map +1 -1
  140. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  141. package/lib-es/logic/estimateFees.js +3 -1
  142. package/lib-es/logic/estimateFees.js.map +1 -1
  143. package/lib-es/logic/getBalance.d.ts +1 -1
  144. package/lib-es/logic/getBalance.d.ts.map +1 -1
  145. package/lib-es/logic/getBalance.js +5 -3
  146. package/lib-es/logic/getBalance.js.map +1 -1
  147. package/lib-es/logic/getBlock.d.ts +1 -1
  148. package/lib-es/logic/getBlock.d.ts.map +1 -1
  149. package/lib-es/logic/getBlock.js +42 -37
  150. package/lib-es/logic/getBlock.js.map +1 -1
  151. package/lib-es/logic/getBlockInfo.d.ts +1 -1
  152. package/lib-es/logic/getBlockInfo.d.ts.map +1 -1
  153. package/lib-es/logic/getBlockInfo.js +3 -1
  154. package/lib-es/logic/getBlockInfo.js.map +1 -1
  155. package/lib-es/logic/index.d.ts +12 -12
  156. package/lib-es/logic/index.d.ts.map +1 -1
  157. package/lib-es/logic/index.js +14 -12
  158. package/lib-es/logic/index.js.map +1 -1
  159. package/lib-es/logic/lastBlock.d.ts +1 -1
  160. package/lib-es/logic/lastBlock.d.ts.map +1 -1
  161. package/lib-es/logic/lastBlock.js +3 -1
  162. package/lib-es/logic/lastBlock.js.map +1 -1
  163. package/lib-es/logic/listOperations.d.ts +1 -1
  164. package/lib-es/logic/listOperations.d.ts.map +1 -1
  165. package/lib-es/logic/listOperations.js +7 -5
  166. package/lib-es/logic/listOperations.js.map +1 -1
  167. package/lib-es/logic/sdkWrapper.d.ts +1 -1
  168. package/lib-es/logic/sdkWrapper.d.ts.map +1 -1
  169. package/lib-es/logic/sdkWrapper.js +4 -2
  170. package/lib-es/logic/sdkWrapper.js.map +1 -1
  171. package/lib-es/logic/utils.d.ts.map +1 -1
  172. package/lib-es/logic/utils.js +6 -4
  173. package/lib-es/logic/utils.js.map +1 -1
  174. package/lib-es/logic/validateAddress.d.ts +1 -1
  175. package/lib-es/logic/validateAddress.d.ts.map +1 -1
  176. package/lib-es/logic/validateAddress.js +3 -1
  177. package/lib-es/logic/validateAddress.js.map +1 -1
  178. package/lib-es/logic/validateIntent.d.ts +2 -2
  179. package/lib-es/logic/validateIntent.d.ts.map +1 -1
  180. package/lib-es/logic/validateIntent.js +30 -28
  181. package/lib-es/logic/validateIntent.js.map +1 -1
  182. package/lib-es/logic/validateMemo.d.ts +1 -1
  183. package/lib-es/logic/validateMemo.d.ts.map +1 -1
  184. package/lib-es/logic/validateMemo.js +8 -6
  185. package/lib-es/logic/validateMemo.js.map +1 -1
  186. package/lib-es/network/horizon.d.ts +6 -7
  187. package/lib-es/network/horizon.d.ts.map +1 -1
  188. package/lib-es/network/horizon.js +57 -104
  189. package/lib-es/network/horizon.js.map +1 -1
  190. package/lib-es/network/horizonErrorBody.d.ts +29 -0
  191. package/lib-es/network/horizonErrorBody.d.ts.map +1 -0
  192. package/lib-es/network/horizonErrorBody.js +134 -0
  193. package/lib-es/network/horizonErrorBody.js.map +1 -0
  194. package/lib-es/network/horizonLedgerErrors.d.ts.map +1 -1
  195. package/lib-es/network/horizonLedgerErrors.js +30 -6
  196. package/lib-es/network/horizonLedgerErrors.js.map +1 -1
  197. package/lib-es/network/index.d.ts +1 -1
  198. package/lib-es/network/index.d.ts.map +1 -1
  199. package/lib-es/network/index.js +3 -1
  200. package/lib-es/network/index.js.map +1 -1
  201. package/lib-es/network/serialization.d.ts +6 -4
  202. package/lib-es/network/serialization.d.ts.map +1 -1
  203. package/lib-es/network/serialization.js +97 -95
  204. package/lib-es/network/serialization.js.map +1 -1
  205. package/lib-es/polyfill.d.ts.map +1 -1
  206. package/lib-es/polyfill.js +7 -5
  207. package/lib-es/polyfill.js.map +1 -1
  208. package/lib-es/supportedFeatures.d.ts +1 -1
  209. package/lib-es/supportedFeatures.d.ts.map +1 -1
  210. package/lib-es/supportedFeatures.js +3 -1
  211. package/lib-es/supportedFeatures.js.map +1 -1
  212. package/lib-es/types/errors.d.ts.map +1 -1
  213. package/lib-es/types/errors.js +15 -13
  214. package/lib-es/types/errors.js.map +1 -1
  215. package/lib-es/types/index.d.ts +2 -2
  216. package/lib-es/types/index.d.ts.map +1 -1
  217. package/lib-es/types/index.js +4 -2
  218. package/lib-es/types/index.js.map +1 -1
  219. package/lib-es/types/model.d.ts +6 -6
  220. package/lib-es/types/model.d.ts.map +1 -1
  221. package/lib-es/types/model.js +7 -5
  222. package/lib-es/types/model.js.map +1 -1
  223. package/package.json +13 -16
  224. package/src/api/index.integ.test.ts +168 -163
  225. package/src/api/index.test.ts +106 -103
  226. package/src/api/index.ts +49 -46
  227. package/src/api/index_error.test.ts +48 -25
  228. package/src/config.ts +15 -12
  229. package/src/constants.ts +4 -1
  230. package/src/globals.d.ts +7 -5
  231. package/src/logic/broadcast.integ.test.ts +22 -19
  232. package/src/logic/broadcast.ts +5 -2
  233. package/src/logic/combine.test.ts +18 -15
  234. package/src/logic/combine.ts +8 -5
  235. package/src/logic/common.ts +9 -6
  236. package/src/logic/craftTransaction.test.ts +76 -75
  237. package/src/logic/craftTransaction.ts +46 -43
  238. package/src/logic/estimateFees.ts +6 -3
  239. package/src/logic/getBalance.test.ts +31 -28
  240. package/src/logic/getBalance.ts +13 -10
  241. package/src/logic/getBlock.integ.test.ts +40 -39
  242. package/src/logic/getBlock.test.ts +462 -415
  243. package/src/logic/getBlock.ts +142 -127
  244. package/src/logic/getBlockInfo.test.ts +72 -69
  245. package/src/logic/getBlockInfo.ts +15 -12
  246. package/src/logic/index.ts +15 -12
  247. package/src/logic/lastBlock.ts +6 -3
  248. package/src/logic/listOperations.ts +14 -11
  249. package/src/logic/listOperations.unit.test.ts +212 -209
  250. package/src/logic/networkWrappers.test.ts +32 -29
  251. package/src/logic/sdkWrapper.ts +19 -16
  252. package/src/logic/utils.ts +13 -10
  253. package/src/logic/validateAddress.test.ts +21 -18
  254. package/src/logic/validateAddress.ts +7 -4
  255. package/src/logic/validateIntent.test.ts +62 -59
  256. package/src/logic/validateIntent.ts +87 -84
  257. package/src/logic/validateMemo.test.ts +50 -47
  258. package/src/logic/validateMemo.ts +16 -13
  259. package/src/network/broadcastTransaction.test.ts +83 -63
  260. package/src/network/fetchLedgerOperations.unit.test.ts +155 -0
  261. package/src/network/horizon.integ.test.ts +45 -42
  262. package/src/network/horizon.test.ts +17 -14
  263. package/src/network/horizon.ts +244 -314
  264. package/src/network/horizon.unit.test.ts +238 -237
  265. package/src/network/horizonErrorBody.test.ts +133 -0
  266. package/src/network/horizonErrorBody.ts +178 -0
  267. package/src/network/horizonLedgerErrors.test.ts +150 -78
  268. package/src/network/horizonLedgerErrors.ts +47 -20
  269. package/src/network/index.ts +4 -1
  270. package/src/network/registerHorizonInterceptors.test.ts +163 -160
  271. package/src/network/serialization.ts +232 -227
  272. package/src/network/serialization.unit.test.ts +801 -798
  273. package/src/polyfill.ts +18 -15
  274. package/src/supportedFeatures.ts +6 -3
  275. package/src/types/errors.ts +33 -32
  276. package/src/types/index.ts +5 -2
  277. package/src/types/model.ts +55 -52
  278. package/tsconfig.json +1 -1
  279. package/.unimportedrc.json +0 -17
  280. package/LICENSE.txt +0 -21
  281. package/src/network/fetchLedgerOperations.nock.unit.test.ts +0 -156
@@ -1,17 +1,21 @@
1
+ // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { RawOperation, StellarMemo } from '../types'
1
5
  import type {
2
6
  AssetInfo,
3
7
  Block,
4
8
  BlockInfo,
5
9
  BlockOperation,
6
10
  BlockTransaction,
7
- } from "@ledgerhq/coin-module-framework/api/index";
8
- import { fetchAllLedgerOperations, fetchLedgerRecord } from "../network";
9
- import type { RawOperation } from "../types";
10
- import { parseAPIValue } from "./common";
11
- import { assertUnreachable } from "./utils";
12
- import BigNumber from "bignumber.js";
11
+ } from '@ledgerhq/coin-module-framework/api/index'
12
+ import BigNumber from 'bignumber.js'
13
+ import { fetchAllLedgerOperations, fetchLedgerRecord } from '../network'
14
+ import { decodeMemo, parseSequence } from '../network/serialization'
15
+ import { parseAPIValue } from './common'
16
+ import { assertUnreachable } from './utils'
13
17
 
14
- const NATIVE_ASSET: AssetInfo = { type: "native", name: "XLM" };
18
+ const NATIVE_ASSET: AssetInfo = { type: 'native', name: 'XLM' }
15
19
 
16
20
  /**
17
21
  * Horizon `type` strings that `getBlock` maps into `BlockOperation` entries.
@@ -20,148 +24,148 @@ const NATIVE_ASSET: AssetInfo = { type: "native", name: "XLM" };
20
24
  * other operation kinds are ignored until a later iteration expands coverage.
21
25
  */
22
26
  const SUPPORTED_GETBLOCK_OP_TYPES = new Set<string>([
23
- "create_account",
24
- "payment",
25
- "path_payment_strict_send",
26
- "path_payment_strict_receive",
27
- "change_trust",
28
- ]);
27
+ 'create_account',
28
+ 'payment',
29
+ 'path_payment_strict_send',
30
+ 'path_payment_strict_receive',
31
+ 'change_trust',
32
+ ])
29
33
 
30
- type RawPathStrictSendOp = Extract<RawOperation, { type: "path_payment_strict_send" }>;
31
- type RawPathStrictReceiveOp = Extract<RawOperation, { type: "path_payment_strict_receive" }> & {
32
- send_max?: string;
33
- };
34
+ type RawPathStrictSendOp = Extract<RawOperation, { type: 'path_payment_strict_send' }>
35
+ type RawPathStrictReceiveOp = Extract<RawOperation, { type: 'path_payment_strict_receive' }> & {
36
+ send_max?: string
37
+ }
34
38
 
35
39
  /** Horizon ops mapped by `getBlock`; kept in sync with `SUPPORTED_GETBLOCK_OP_TYPES`. */
36
40
  type SupportedGetBlockOperation = Extract<
37
41
  RawOperation,
38
- | { type: "payment" }
39
- | { type: "create_account" }
40
- | { type: "change_trust" }
41
- | { type: "path_payment_strict_send" }
42
- | { type: "path_payment_strict_receive" }
43
- >;
42
+ | { type: 'payment' }
43
+ | { type: 'create_account' }
44
+ | { type: 'change_trust' }
45
+ | { type: 'path_payment_strict_send' }
46
+ | { type: 'path_payment_strict_receive' }
47
+ >
44
48
 
45
49
  function pathStrictDualLegTransfers(params: {
46
- fromAddr: string | undefined;
47
- toAddr: string | undefined;
48
- sourceAsset: AssetInfo;
49
- destAsset: AssetInfo;
50
- sourceStroops: bigint;
51
- destStroops: bigint;
50
+ fromAddr: string | undefined
51
+ toAddr: string | undefined
52
+ sourceAsset: AssetInfo
53
+ destAsset: AssetInfo
54
+ sourceStroops: bigint
55
+ destStroops: bigint
52
56
  }): BlockOperation[] {
53
- const { fromAddr, toAddr, sourceAsset, destAsset, sourceStroops, destStroops } = params;
54
- const ops: BlockOperation[] = [];
57
+ const { fromAddr, toAddr, sourceAsset, destAsset, sourceStroops, destStroops } = params
58
+ const ops: BlockOperation[] = []
55
59
  if (fromAddr && sourceStroops !== 0n) {
56
60
  ops.push({
57
- type: "transfer",
61
+ type: 'transfer',
58
62
  address: fromAddr,
59
63
  ...(toAddr && { peer: toAddr }),
60
64
  asset: sourceAsset,
61
65
  amount: -sourceStroops,
62
- });
66
+ })
63
67
  }
64
68
  if (toAddr && destStroops !== 0n) {
65
69
  ops.push({
66
- type: "transfer",
70
+ type: 'transfer',
67
71
  address: toAddr,
68
72
  ...(fromAddr && { peer: fromAddr }),
69
73
  asset: destAsset,
70
74
  amount: destStroops,
71
- });
75
+ })
72
76
  }
73
- return ops;
77
+ return ops
74
78
  }
75
79
 
76
80
  function toStroops(amountStr: string | undefined): bigint {
77
81
  if (!amountStr) {
78
- return 0n;
82
+ return 0n
79
83
  }
80
- return BigInt(parseAPIValue(amountStr).integerValue(BigNumber.ROUND_FLOOR).toFixed(0));
84
+ return BigInt(parseAPIValue(amountStr).integerValue(BigNumber.ROUND_FLOOR).toFixed(0))
81
85
  }
82
86
 
83
87
  function assetFromHorizonFields(
84
88
  assetType: string | undefined,
85
89
  assetCode: string | undefined,
86
- assetIssuer: string | undefined,
90
+ assetIssuer: string | undefined
87
91
  ): AssetInfo {
88
- if (!assetType || assetType === "native") {
89
- return NATIVE_ASSET;
92
+ if (!assetType || assetType === 'native') {
93
+ return NATIVE_ASSET
90
94
  }
91
95
  return {
92
- type: "token",
93
- assetReference: assetCode ?? "",
94
- assetOwner: assetIssuer ?? "",
95
- };
96
+ type: 'token',
97
+ assetReference: assetCode ?? '',
98
+ assetOwner: assetIssuer ?? '',
99
+ }
96
100
  }
97
101
 
98
102
  function isSupportedGetBlockOperation(op: RawOperation): op is SupportedGetBlockOperation {
99
- return SUPPORTED_GETBLOCK_OP_TYPES.has(op.type);
103
+ return SUPPORTED_GETBLOCK_OP_TYPES.has(op.type)
100
104
  }
101
105
 
102
106
  function orderTransactionHashes(rawOps: RawOperation[]): string[] {
103
- const order: string[] = [];
104
- const seen = new Set<string>();
107
+ const order: string[] = []
108
+ const seen = new Set<string>()
105
109
  for (const op of rawOps) {
106
- const h = op.transaction_hash;
110
+ const h = op.transaction_hash
107
111
  if (!seen.has(h)) {
108
- seen.add(h);
109
- order.push(h);
112
+ seen.add(h)
113
+ order.push(h)
110
114
  }
111
115
  }
112
- return order;
116
+ return order
113
117
  }
114
118
 
115
119
  function groupOpsByHash(rawOps: RawOperation[]): Map<string, RawOperation[]> {
116
- const map = new Map<string, RawOperation[]>();
120
+ const map = new Map<string, RawOperation[]>()
117
121
  for (const op of rawOps) {
118
- const h = op.transaction_hash;
119
- const list = map.get(h);
120
- if (list) list.push(op);
121
- else map.set(h, [op]);
122
+ const h = op.transaction_hash
123
+ const list = map.get(h)
124
+ if (list) list.push(op)
125
+ else map.set(h, [op])
122
126
  }
123
- return map;
127
+ return map
124
128
  }
125
129
 
126
130
  function mapPaymentLikeTransfer(
127
131
  fromAddr: string | undefined,
128
132
  toAddr: string | undefined,
129
133
  amountStr: string | undefined,
130
- asset: AssetInfo,
134
+ asset: AssetInfo
131
135
  ): BlockOperation[] {
132
- const amount = toStroops(amountStr);
136
+ const amount = toStroops(amountStr)
133
137
  if (amount === 0n) {
134
- return [];
138
+ return []
135
139
  }
136
- const ops: BlockOperation[] = [];
140
+ const ops: BlockOperation[] = []
137
141
  if (fromAddr) {
138
142
  ops.push({
139
- type: "transfer",
143
+ type: 'transfer',
140
144
  address: fromAddr,
141
145
  ...(toAddr && { peer: toAddr }),
142
146
  asset,
143
147
  amount: -amount,
144
- });
148
+ })
145
149
  }
146
150
  if (toAddr) {
147
151
  ops.push({
148
- type: "transfer",
152
+ type: 'transfer',
149
153
  address: toAddr,
150
154
  ...(fromAddr && { peer: fromAddr }),
151
155
  asset,
152
156
  amount,
153
- });
157
+ })
154
158
  }
155
- return ops;
159
+ return ops
156
160
  }
157
161
 
158
162
  function blockOperationsPathStrictSend(op: RawPathStrictSendOp): BlockOperation[] {
159
- const destAsset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer);
163
+ const destAsset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer)
160
164
  const sourceAsset = assetFromHorizonFields(
161
165
  op.source_asset_type,
162
166
  op.source_asset_code,
163
- op.source_asset_issuer,
164
- );
167
+ op.source_asset_issuer
168
+ )
165
169
  return pathStrictDualLegTransfers({
166
170
  fromAddr: op.from,
167
171
  toAddr: op.to,
@@ -169,18 +173,18 @@ function blockOperationsPathStrictSend(op: RawPathStrictSendOp): BlockOperation[
169
173
  destAsset,
170
174
  sourceStroops: toStroops(op.source_amount),
171
175
  destStroops: toStroops(op.amount),
172
- });
176
+ })
173
177
  }
174
178
 
175
179
  function blockOperationsPathStrictReceive(op: RawPathStrictReceiveOp): BlockOperation[] {
176
- const destAsset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer);
180
+ const destAsset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer)
177
181
  const sourceAsset = assetFromHorizonFields(
178
182
  op.source_asset_type,
179
183
  op.source_asset_code,
180
- op.source_asset_issuer,
181
- );
184
+ op.source_asset_issuer
185
+ )
182
186
  // Horizon reports actual source debit in `source_amount`; `send_max` is only the envelope cap.
183
- const sourceStroops = toStroops(op.source_amount ?? op.send_max);
187
+ const sourceStroops = toStroops(op.source_amount ?? op.send_max)
184
188
  return pathStrictDualLegTransfers({
185
189
  fromAddr: op.from,
186
190
  toAddr: op.to,
@@ -188,63 +192,74 @@ function blockOperationsPathStrictReceive(op: RawPathStrictReceiveOp): BlockOper
188
192
  destAsset,
189
193
  sourceStroops,
190
194
  destStroops: toStroops(op.amount),
191
- });
195
+ })
192
196
  }
193
197
 
194
- function mapSupportedOperationToBlockOperations(op: SupportedGetBlockOperation): BlockOperation[] {
195
- if (op.type === "payment") {
196
- const asset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer);
197
- const toAddr = op.to_muxed || op.to;
198
- return mapPaymentLikeTransfer(op.from, toAddr, op.amount, asset);
198
+ type TxContext = {
199
+ memo: StellarMemo | undefined
200
+ sequence: string | undefined
201
+ feeCharged: string
202
+ }
203
+
204
+ function mapSupportedOperationToBlockOperations(
205
+ op: SupportedGetBlockOperation,
206
+ txCtx: TxContext
207
+ ): BlockOperation[] {
208
+ if (op.type === 'payment') {
209
+ const asset = assetFromHorizonFields(op.asset_type, op.asset_code, op.asset_issuer)
210
+ const toAddr = op.to_muxed || op.to
211
+ return mapPaymentLikeTransfer(op.from, toAddr, op.amount, asset)
199
212
  }
200
- if (op.type === "create_account") {
201
- return mapPaymentLikeTransfer(op.funder, op.account, op.starting_balance, NATIVE_ASSET);
213
+ if (op.type === 'create_account') {
214
+ return mapPaymentLikeTransfer(op.funder, op.account, op.starting_balance, NATIVE_ASSET)
202
215
  }
203
- if (op.type === "change_trust") {
204
- const isOptOut = new BigNumber(op.limit || "0").eq(0);
216
+ if (op.type === 'change_trust') {
217
+ const isOptOut = new BigNumber(op.limit || '0').eq(0)
218
+ // Align with listOperations: assetAmount = fee_charged (not the trustline limit)
205
219
  return [
206
220
  {
207
- type: "other",
208
- ledgerOpType: isOptOut ? "OPT_OUT" : "OPT_IN",
209
- trustor: op.trustor,
210
- assetCode: op.asset_code,
211
- assetIssuer: op.asset_issuer,
212
- limit: op.limit,
221
+ type: 'other',
222
+ ledgerOpType: isOptOut ? 'OPT_OUT' : 'OPT_IN',
223
+ assetAmount: op.asset_code ? txCtx.feeCharged : undefined,
224
+ ...(txCtx.memo && { memo: txCtx.memo }),
225
+ ...(txCtx.sequence && { sequence: txCtx.sequence }),
213
226
  },
214
- ];
227
+ ]
215
228
  }
216
- if (op.type === "path_payment_strict_send") {
217
- return blockOperationsPathStrictSend(op);
229
+ if (op.type === 'path_payment_strict_send') {
230
+ return blockOperationsPathStrictSend(op)
218
231
  }
219
- if (op.type === "path_payment_strict_receive") {
220
- return blockOperationsPathStrictReceive(op);
232
+ if (op.type === 'path_payment_strict_receive') {
233
+ return blockOperationsPathStrictReceive(op)
221
234
  }
222
- return assertUnreachable(op);
235
+ return assertUnreachable(op)
223
236
  }
224
237
 
225
238
  async function blockTransactionForHash(
226
239
  hash: string,
227
- ops: RawOperation[],
240
+ ops: RawOperation[]
228
241
  ): Promise<BlockTransaction | null> {
229
- if (ops.length === 0) return null;
230
- const failed = !ops[0].transaction_successful;
242
+ if (ops.length === 0) return null
243
+ const failed = !ops[0].transaction_successful
231
244
 
232
- let blockOperations: BlockOperation[] = [];
245
+ const tx = await ops[0].transaction()
246
+ const txCtx: TxContext = {
247
+ memo: decodeMemo(tx),
248
+ sequence: parseSequence(tx.source_account_sequence),
249
+ feeCharged: new BigNumber(tx.fee_charged || '0').toString(),
250
+ }
251
+
252
+ let blockOperations: BlockOperation[] = []
233
253
  if (!failed) {
234
254
  for (const op of ops) {
235
255
  if (isSupportedGetBlockOperation(op)) {
236
- blockOperations = blockOperations.concat(mapSupportedOperationToBlockOperations(op));
256
+ blockOperations = blockOperations.concat(mapSupportedOperationToBlockOperations(op, txCtx))
237
257
  }
238
258
  }
239
259
  }
240
260
 
241
- if (!failed && blockOperations.length === 0) {
242
- return null;
243
- }
244
-
245
- const tx = await ops[0].transaction();
246
- const fees = BigInt(tx.fee_charged || "0");
247
- const feesPayer = tx.fee_account || tx.source_account;
261
+ const fees = BigInt(tx.fee_charged || '0')
262
+ const feesPayer = tx.fee_account || tx.source_account
248
263
 
249
264
  return {
250
265
  hash,
@@ -252,24 +267,24 @@ async function blockTransactionForHash(
252
267
  fees,
253
268
  ...(feesPayer ? { feesPayer } : {}),
254
269
  operations: blockOperations,
255
- };
270
+ }
256
271
  }
257
272
 
258
273
  async function buildBlockTransactions(rawOps: RawOperation[]): Promise<BlockTransaction[]> {
259
- const order = orderTransactionHashes(rawOps);
260
- const byHash = groupOpsByHash(rawOps);
261
- const out: BlockTransaction[] = [];
274
+ const order = orderTransactionHashes(rawOps)
275
+ const byHash = groupOpsByHash(rawOps)
276
+ const out: BlockTransaction[] = []
262
277
 
263
278
  for (const hash of order) {
264
279
  // `order` and `byHash` are built from the same `rawOps`; every ordered hash has a non-empty op list.
265
- const ops = byHash.get(hash)!;
266
- const row = await blockTransactionForHash(hash, ops);
280
+ const ops = byHash.get(hash)!
281
+ const row = await blockTransactionForHash(hash, ops)
267
282
  if (row) {
268
- out.push(row);
283
+ out.push(row)
269
284
  }
270
285
  }
271
286
 
272
- return out;
287
+ return out
273
288
  }
274
289
 
275
290
  /**
@@ -284,22 +299,22 @@ async function buildBlockTransactions(rawOps: RawOperation[]): Promise<BlockTran
284
299
  */
285
300
  export async function getBlock(height: number): Promise<Block> {
286
301
  if (!Number.isSafeInteger(height) || height <= 0) {
287
- throw new Error(`getBlock: height must be a positive integer, got ${height}`);
302
+ throw new Error(`getBlock: height must be a positive integer, got ${height}`)
288
303
  }
289
304
 
290
305
  const [ledger, rawOps] = await Promise.all([
291
306
  fetchLedgerRecord(height),
292
307
  fetchAllLedgerOperations(height),
293
- ]);
308
+ ])
294
309
 
295
- let parent: BlockInfo["parent"];
310
+ let parent: BlockInfo['parent']
296
311
  if (height > 1) {
297
- const prevHash = (ledger as { prev_hash?: string }).prev_hash;
312
+ const prevHash = (ledger as { prev_hash?: string }).prev_hash
298
313
  if (prevHash) {
299
- parent = { height: height - 1, hash: prevHash };
314
+ parent = { height: height - 1, hash: prevHash }
300
315
  } else {
301
- const parentLedger = await fetchLedgerRecord(height - 1);
302
- parent = { height: parentLedger.sequence, hash: parentLedger.hash };
316
+ const parentLedger = await fetchLedgerRecord(height - 1)
317
+ parent = { height: parentLedger.sequence, hash: parentLedger.hash }
303
318
  }
304
319
  }
305
320
 
@@ -308,8 +323,8 @@ export async function getBlock(height: number): Promise<Block> {
308
323
  hash: ledger.hash,
309
324
  time: new Date(ledger.closed_at),
310
325
  ...(parent && { parent }),
311
- };
326
+ }
312
327
 
313
- const transactions = await buildBlockTransactions(rawOps);
314
- return { info, transactions };
328
+ const transactions = await buildBlockTransactions(rawOps)
329
+ return { info, transactions }
315
330
  }
@@ -1,21 +1,24 @@
1
- import { getBlockInfo } from "./getBlockInfo";
2
- import * as network from "../network";
1
+ // SPDX-FileCopyrightText: © 2026 LEDGER SAS
2
+ // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- jest.mock("../network", () => ({
5
- ...jest.requireActual<typeof import("../network")>("../network"),
4
+ import * as network from '../network'
5
+ import { getBlockInfo } from './getBlockInfo'
6
+
7
+ jest.mock('../network', () => ({
8
+ ...jest.requireActual<typeof import('../network')>('../network'),
6
9
  fetchLedgerRecord: jest.fn(),
7
- }));
10
+ }))
8
11
 
9
12
  const fetchLedgerRecordMock = network.fetchLedgerRecord as jest.MockedFunction<
10
13
  typeof network.fetchLedgerRecord
11
- >;
14
+ >
12
15
 
13
- describe("getBlockInfo", () => {
16
+ describe('getBlockInfo', () => {
14
17
  beforeEach(() => {
15
- jest.clearAllMocks();
16
- });
18
+ jest.clearAllMocks()
19
+ })
17
20
 
18
- it("returns block info with parent when height > 1 (uses ledger prev_hash)", async () => {
21
+ it('returns block info with parent when height > 1 (uses ledger prev_hash)', async () => {
19
22
  fetchLedgerRecordMock.mockImplementation(
20
23
  async (seq: number) =>
21
24
  ({
@@ -23,94 +26,94 @@ describe("getBlockInfo", () => {
23
26
  hash: `hash-${seq}`,
24
27
  closed_at: `2020-01-0${seq}T00:00:00Z`,
25
28
  ...(seq > 1 && { prev_hash: `hash-${seq - 1}` }),
26
- }) as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>,
27
- );
29
+ }) as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>
30
+ )
28
31
 
29
- const result = await getBlockInfo(5);
32
+ const result = await getBlockInfo(5)
30
33
 
31
- expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(1);
32
- expect(fetchLedgerRecordMock).toHaveBeenCalledWith(5);
34
+ expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(1)
35
+ expect(fetchLedgerRecordMock).toHaveBeenCalledWith(5)
33
36
  expect(result).toEqual({
34
37
  height: 5,
35
- hash: "hash-5",
36
- time: new Date("2020-01-05T00:00:00Z"),
37
- parent: { height: 4, hash: "hash-4" },
38
- });
39
- });
38
+ hash: 'hash-5',
39
+ time: new Date('2020-01-05T00:00:00Z'),
40
+ parent: { height: 4, hash: 'hash-4' },
41
+ })
42
+ })
40
43
 
41
- it("falls back to fetching parent ledger when prev_hash is missing", async () => {
44
+ it('falls back to fetching parent ledger when prev_hash is missing', async () => {
42
45
  fetchLedgerRecordMock.mockImplementation(
43
46
  async (seq: number) =>
44
47
  ({
45
48
  sequence: seq,
46
49
  hash: `hash-${seq}`,
47
50
  closed_at: `2020-01-0${seq}T00:00:00Z`,
48
- }) as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>,
49
- );
51
+ }) as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>
52
+ )
50
53
 
51
- const result = await getBlockInfo(5);
54
+ const result = await getBlockInfo(5)
52
55
 
53
- expect(fetchLedgerRecordMock).toHaveBeenCalledWith(5);
54
- expect(fetchLedgerRecordMock).toHaveBeenCalledWith(4);
55
- expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(2);
56
+ expect(fetchLedgerRecordMock).toHaveBeenCalledWith(5)
57
+ expect(fetchLedgerRecordMock).toHaveBeenCalledWith(4)
58
+ expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(2)
56
59
  expect(result).toEqual({
57
60
  height: 5,
58
- hash: "hash-5",
59
- time: new Date("2020-01-05T00:00:00Z"),
60
- parent: { height: 4, hash: "hash-4" },
61
- });
62
- });
61
+ hash: 'hash-5',
62
+ time: new Date('2020-01-05T00:00:00Z'),
63
+ parent: { height: 4, hash: 'hash-4' },
64
+ })
65
+ })
63
66
 
64
- it("omits parent for genesis ledger sequence 1", async () => {
67
+ it('omits parent for genesis ledger sequence 1', async () => {
65
68
  fetchLedgerRecordMock.mockResolvedValue({
66
69
  sequence: 1,
67
- hash: "genesis-hash",
68
- closed_at: "2015-09-30T00:00:00Z",
69
- } as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>);
70
+ hash: 'genesis-hash',
71
+ closed_at: '2015-09-30T00:00:00Z',
72
+ } as unknown as Awaited<ReturnType<typeof network.fetchLedgerRecord>>)
70
73
 
71
- const result = await getBlockInfo(1);
74
+ const result = await getBlockInfo(1)
72
75
 
73
- expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(1);
74
- expect(fetchLedgerRecordMock).toHaveBeenCalledWith(1);
75
- expect(result.parent).toBeUndefined();
76
- expect(result.height).toBe(1);
77
- });
76
+ expect(fetchLedgerRecordMock).toHaveBeenCalledTimes(1)
77
+ expect(fetchLedgerRecordMock).toHaveBeenCalledWith(1)
78
+ expect(result.parent).toBeUndefined()
79
+ expect(result.height).toBe(1)
80
+ })
78
81
 
79
- describe("height validation", () => {
80
- it("rejects height 0", async () => {
82
+ describe('height validation', () => {
83
+ it('rejects height 0', async () => {
81
84
  await expect(getBlockInfo(0)).rejects.toThrow(
82
- "getBlockInfo: height must be a positive integer, got 0",
83
- );
84
- });
85
+ 'getBlockInfo: height must be a positive integer, got 0'
86
+ )
87
+ })
85
88
 
86
- it("rejects negative height", async () => {
89
+ it('rejects negative height', async () => {
87
90
  await expect(getBlockInfo(-3)).rejects.toThrow(
88
- "getBlockInfo: height must be a positive integer, got -3",
89
- );
90
- });
91
+ 'getBlockInfo: height must be a positive integer, got -3'
92
+ )
93
+ })
91
94
 
92
- it("rejects non-integer height", async () => {
95
+ it('rejects non-integer height', async () => {
93
96
  await expect(getBlockInfo(3.5)).rejects.toThrow(
94
- "getBlockInfo: height must be a positive integer, got 3.5",
95
- );
96
- });
97
+ 'getBlockInfo: height must be a positive integer, got 3.5'
98
+ )
99
+ })
97
100
 
98
- it("rejects NaN", async () => {
101
+ it('rejects NaN', async () => {
99
102
  await expect(getBlockInfo(Number.NaN)).rejects.toThrow(
100
- "getBlockInfo: height must be a positive integer, got NaN",
101
- );
102
- });
103
+ 'getBlockInfo: height must be a positive integer, got NaN'
104
+ )
105
+ })
103
106
 
104
- it("rejects unsafe integer", async () => {
107
+ it('rejects unsafe integer', async () => {
105
108
  await expect(getBlockInfo(Number.MAX_SAFE_INTEGER + 1)).rejects.toThrow(
106
- "getBlockInfo: height must be a positive integer",
107
- );
108
- });
109
+ 'getBlockInfo: height must be a positive integer'
110
+ )
111
+ })
109
112
 
110
- it("rejects Infinity", async () => {
113
+ it('rejects Infinity', async () => {
111
114
  await expect(getBlockInfo(Number.POSITIVE_INFINITY)).rejects.toThrow(
112
- "getBlockInfo: height must be a positive integer, got Infinity",
113
- );
114
- });
115
- });
116
- });
115
+ 'getBlockInfo: height must be a positive integer, got Infinity'
116
+ )
117
+ })
118
+ })
119
+ })