@mojaloop/central-ledger 19.14.0 → 20.1.0

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 (394) hide show
  1. package/.grype.yaml +3 -0
  2. package/.license-scanner.json +16 -0
  3. package/.ncurc.yaml +19 -13
  4. package/.nvmrc +1 -1
  5. package/.nycrc.yml +14 -2
  6. package/CHANGELOG.md +51 -0
  7. package/Dockerfile +13 -10
  8. package/README.md +39 -89
  9. package/audit-ci.jsonc +1 -1
  10. package/config/default.json +60 -3
  11. package/config/deprecated.default-settlement.json +160 -0
  12. package/docker/ml-api-adapter/default.json +28 -0
  13. package/docker-compose.yml +6 -0
  14. package/nodemon.json +6 -0
  15. package/package.json +128 -64
  16. package/{sbom-v19.13.2.csv → sbom-v20.0.0.csv} +886 -736
  17. package/scripts/_wait4_all.js +5 -5
  18. package/scripts/transferSettlementTemp/interchangeFeeCalculation.js +51 -0
  19. package/src/api/index.ts +30 -0
  20. package/src/api/interface/swagger.json +2249 -2054
  21. package/src/api/participants/routes.js +1 -1
  22. package/src/api/root/handler.int.ts +55 -0
  23. package/src/api/routes.js +7 -6
  24. package/src/api/server.ts +27 -0
  25. package/src/api/server.unit.ts +16 -0
  26. package/src/api/settlementModels/routes.js +4 -4
  27. package/src/domain/participant/index.d.ts +47 -0
  28. package/src/domain/participant/index.int.ts +724 -0
  29. package/src/domain/participant/index.js +4 -5
  30. package/src/domain/position/fx-prepare.js +1 -1
  31. package/src/domain/position/fx-timeout-reserved.js +6 -0
  32. package/src/domain/position/prepare.js +1 -1
  33. package/src/handlers/admin/handler.js +1 -1
  34. package/src/handlers/index.js +30 -0
  35. package/src/handlers/positions/handler.js +0 -2
  36. package/src/handlers/positions/handlerBatch.js +5 -5
  37. package/src/handlers/register.js +44 -29
  38. package/src/handlers/timeouts/handler.js +1 -4
  39. package/src/handlers/transfers/fx-abort.int.ts +209 -0
  40. package/src/handlers/transfers/fx-handlers.int.ts +275 -0
  41. package/src/handlers/transfers/fx-timeout.int.ts +284 -0
  42. package/src/handlers/transfers/handler.js +1 -3
  43. package/src/handlers/transfers/handlers.int.ts +538 -0
  44. package/src/handlers/transfers/prepare.int.ts +1283 -0
  45. package/src/handlers/transfers/prepare.js +2 -2
  46. package/src/knexfile.js +19 -0
  47. package/src/lib/cache.js +1 -1
  48. package/src/lib/config/index.ts +60 -0
  49. package/src/lib/config/resolver.ts +231 -0
  50. package/src/lib/config/resolver.unit.ts +72 -0
  51. package/src/lib/config/types.ts +372 -0
  52. package/src/lib/config/util.ts +523 -0
  53. package/src/lib/config/util.unit.ts +825 -0
  54. package/src/lib/migrator.js +3 -3
  55. package/src/lib/proxyCache.js +3 -3
  56. package/{migrations → src/migrations}/100100_event.js +1 -1
  57. package/{migrations → src/migrations}/110100_contactType.js +1 -1
  58. package/{migrations → src/migrations}/110200_currency.js +1 -1
  59. package/{migrations → src/migrations}/110201_currency-scale.js +1 -1
  60. package/{migrations → src/migrations}/110300_endpointType.js +1 -1
  61. package/{migrations → src/migrations}/110400_ledgerEntryType.js +1 -1
  62. package/{migrations → src/migrations}/110450_ledgerAccountType.js +1 -1
  63. package/{migrations → src/migrations}/110500_participantLimitType.js +1 -1
  64. package/{migrations → src/migrations}/110600_transferParticipantRoleType.js +1 -1
  65. package/{migrations → src/migrations}/110700_transferState.js +1 -1
  66. package/{migrations → src/migrations}/110900_settlementState.js +1 -1
  67. package/{migrations → src/migrations}/112000_bulkTransferState.js +1 -1
  68. package/{migrations → src/migrations}/112100_bulkProcessingState.js +1 -1
  69. package/{migrations → src/migrations}/200100_participant.js +1 -1
  70. package/{migrations → src/migrations}/200200_participantContact.js +1 -1
  71. package/{migrations → src/migrations}/200300_participantEndpoint.js +1 -1
  72. package/{migrations → src/migrations}/200400_participantParty.js +1 -1
  73. package/{migrations → src/migrations}/200600_token.js +1 -1
  74. package/{migrations → src/migrations}/200601_token-indexes.js +1 -1
  75. package/{migrations → src/migrations}/300100_transferDuplicateCheck.js +2 -2
  76. package/{migrations → src/migrations}/300150_bulkTransferDuplicateCheck.js +2 -2
  77. package/{migrations → src/migrations}/300200_transfer.js +2 -2
  78. package/{migrations → src/migrations}/300202_transfer-decimal184.js +1 -1
  79. package/{migrations → src/migrations}/300250_bulkTransfer.js +2 -3
  80. package/{migrations → src/migrations}/300275_bulkTransferAssociation.js +2 -3
  81. package/{migrations → src/migrations}/300300_ilpPacket.js +1 -1
  82. package/{migrations → src/migrations}/300400_transferStateChange.js +1 -1
  83. package/{migrations → src/migrations}/300450_bulkTransferStateChange.js +1 -1
  84. package/{migrations → src/migrations}/300500_segment.js +1 -1
  85. package/{migrations → src/migrations}/310100_participantCurrency.js +1 -1
  86. package/{migrations → src/migrations}/310200_transferParticipant.js +1 -1
  87. package/{migrations → src/migrations}/310202_transferParticipant-decimal184.js +1 -1
  88. package/{migrations → src/migrations}/310204_transferParticipant-participantId.js +2 -2
  89. package/{migrations → src/migrations}/310300_participantPosition.js +1 -1
  90. package/{migrations → src/migrations}/310302_participantPosition-decimal184.js +1 -1
  91. package/{migrations → src/migrations}/310400_participantPositionChange.js +1 -1
  92. package/{migrations → src/migrations}/310402_participantPositionChange-decimal184.js +1 -1
  93. package/{migrations → src/migrations}/310403_participantPositionChange-participantCurrencyId.js +2 -2
  94. package/{migrations → src/migrations}/310404_participantPositionChange-change.js +2 -2
  95. package/{migrations → src/migrations}/310500_participantLimit.js +1 -1
  96. package/{migrations → src/migrations}/310502_participantLimit-decimal184.js +1 -1
  97. package/{migrations → src/migrations}/400100_settlementWindow.js +1 -1
  98. package/{migrations → src/migrations}/400200_settlement.js +1 -1
  99. package/{migrations → src/migrations}/400300_settlementSettlementWindow.js +1 -1
  100. package/{migrations → src/migrations}/400301_settlementSettlementWindow-indexes.js +0 -1
  101. package/{migrations → src/migrations}/400400_settlementWindowStateChange.js +1 -1
  102. package/{migrations → src/migrations}/400500_settlementTransferParticipant.js +1 -1
  103. package/{migrations → src/migrations}/400502_settlementTransferParticipant-decimal184.js +1 -1
  104. package/{migrations → src/migrations}/400600_settlementParticipantCurrency.js +1 -1
  105. package/{migrations → src/migrations}/400602_settlementParticipantCurrency-decimal184.js +1 -1
  106. package/{migrations → src/migrations}/400700_settlementParticipantCurrencyStateChange.js +1 -1
  107. package/{migrations → src/migrations}/400800_settlementStateChange.js +1 -1
  108. package/{migrations → src/migrations}/400900_settlementWindowContent.js +1 -1
  109. package/{migrations → src/migrations}/401000_settlementWindowContentStateChange.js +1 -1
  110. package/{migrations → src/migrations}/401001_settlementWindowContentStateChange-indexes.js +6 -6
  111. package/{migrations → src/migrations}/401200_settlementContentAggregation.js +1 -1
  112. package/{migrations → src/migrations}/401300_settlementModel.js +1 -1
  113. package/{migrations → src/migrations}/410100_transferFulfilmentDuplicateCheck.js +1 -1
  114. package/{migrations → src/migrations}/410102_transferFulfilmentDuplicateCheck-dropForeignTransferId.js +1 -1
  115. package/{migrations → src/migrations}/410103_transferFulfilmentDuplicateCheck-hashNullable.js +1 -1
  116. package/{migrations → src/migrations}/410200_transferFulfilment.js +1 -1
  117. package/{migrations → src/migrations}/410202_transferFulfilment-ilpFulfilment.js +1 -1
  118. package/{migrations → src/migrations}/410203_transferFulfilment-foreignTransferFulfilmentId.js +1 -1
  119. package/{migrations → src/migrations}/410240_bulkTransferFulfilmentDuplicateCheck.js +1 -1
  120. package/{migrations → src/migrations}/410250_bulkTransferFulfilment.js +1 -1
  121. package/{migrations → src/migrations}/410300_transferExtension.js +1 -1
  122. package/{migrations → src/migrations}/410302_transferExtension-transferErrorId.js +1 -1
  123. package/{migrations → src/migrations}/410350_bulkTransferExtension.js +1 -1
  124. package/{migrations → src/migrations}/410400_transferTimeout.js +1 -1
  125. package/{migrations → src/migrations}/410450_transferErrorDuplicateCheck.js +1 -1
  126. package/{migrations → src/migrations}/410500_transferError.js +1 -1
  127. package/{migrations → src/migrations}/410502_transferError-foreignTransferErrorDuplicateCheckId.js +1 -1
  128. package/{migrations → src/migrations}/410550_bulkTransferError.js +1 -1
  129. package/{migrations → src/migrations}/410600_expiringTransfer.js +1 -1
  130. package/{migrations → src/migrations}/500401_quote-hotfix-2719.js +8 -8
  131. package/{migrations → src/migrations}/500501_feature-fixSubIdRef.js +12 -12
  132. package/{migrations → src/migrations}/500601_party-2480.js +3 -3
  133. package/{migrations → src/migrations}/501001_quoteExtension-quote-service-174.js +1 -2
  134. package/{migrations → src/migrations}/501002_quoteExtension-2522.js +3 -3
  135. package/{migrations → src/migrations}/600010_fxTransferType.js +1 -1
  136. package/{migrations → src/migrations}/600012_fxParticipantCurrencyType.js +1 -1
  137. package/{migrations → src/migrations}/600100_fxTransferDuplicateCheck.js +2 -2
  138. package/{migrations → src/migrations}/600110_fxTransferErrorDuplicateCheck.js.js +1 -1
  139. package/{migrations → src/migrations}/600200_fxTransfer.js +2 -2
  140. package/{migrations → src/migrations}/600400_fxTransferStateChange.js +1 -1
  141. package/{migrations → src/migrations}/600501_fxWatchList.js +2 -2
  142. package/{migrations → src/migrations}/600600_fxTransferFulfilmentDuplicateCheck.js +1 -1
  143. package/{migrations → src/migrations}/600700_fxTransferFulfilment.js +1 -1
  144. package/{migrations → src/migrations}/600800_fxTransferExtension.js +1 -1
  145. package/{migrations → src/migrations}/601400_fxTransferTimeout.js +1 -1
  146. package/{migrations → src/migrations}/601500_fxTransferError.js +1 -1
  147. package/{migrations → src/migrations}/610200_fxTransferParticipant.js +1 -1
  148. package/{migrations → src/migrations}/610202_fxTransferParticipant-participantId.js +2 -2
  149. package/{migrations → src/migrations}/610403_participantPositionChange-fxTransfer.js +1 -1
  150. package/{migrations → src/migrations}/800100_quotePartyView.js +1 -1
  151. package/{migrations → src/migrations}/800101_feature-fixSubIdRef.js +1 -1
  152. package/{migrations → src/migrations}/800200_quoteView.js +1 -1
  153. package/{migrations → src/migrations}/800300_quoteResponseView.js +1 -1
  154. package/{migrations → src/migrations}/900100_foreign-keys.js +18 -18
  155. package/{migrations → src/migrations}/900101_foreign-keys.js +6 -6
  156. package/{migrations → src/migrations}/910100_feature904DDL.js +7 -7
  157. package/{migrations → src/migrations}/910101_feature904DataMigration.js +1 -1
  158. package/{migrations → src/migrations}/910102_feature949DataMigration.js +1 -1
  159. package/{migrations → src/migrations}/910103_feature1093SettlementByCurrency.js +3 -3
  160. package/{migrations → src/migrations}/940101_quotePartyIdInfoExtension-2522.js +25 -25
  161. package/{migrations → src/migrations}/950101_transferParticipantStateChange.js +2 -2
  162. package/{migrations → src/migrations}/950102_settlementModel-adjustPosition.js +2 -2
  163. package/{migrations → src/migrations}/950103_dropTransferParticipantStateChange.js +3 -3
  164. package/{migrations → src/migrations}/950104_settlementModel-settlementAccountTypeId.js +3 -3
  165. package/src/migrations/950107_settlementWindowContent_foreign_settlementModel.js +63 -0
  166. package/{migrations → src/migrations}/950108_participantProxy.js +1 -2
  167. package/{migrations → src/migrations}/950120_fxQuoteConversionTerms_sourceAmount_nullable.js +1 -1
  168. package/{migrations → src/migrations}/960100_create_externalParticipant.js +2 -2
  169. package/{migrations → src/migrations}/960110_alter_transferParticipant__addFiled_externalParticipantId.js +2 -2
  170. package/{migrations → src/migrations}/961001_transferForwarded.js +1 -1
  171. package/{migrations → src/migrations}/961002_fxTransferForwarded.js +1 -1
  172. package/src/models/participant/externalParticipant.int.ts +85 -0
  173. package/src/models/participant/facade.js +4 -2
  174. package/src/models/participant/participantCurrencyCached.js +1 -1
  175. package/src/models/position/facade.js +8 -2
  176. package/src/models/transfer/facade.int.ts +139 -0
  177. package/src/models/transfer/facade.js +8 -3
  178. package/src/models/transfer/transferDuplicateCheck.js +2 -2
  179. package/src/models/transfer/transferError.int.ts +112 -0
  180. package/src/models/transfer/transferExtension.int.ts +137 -0
  181. package/src/models/transfer/transferStateChange.int.ts +117 -0
  182. package/{seeds → src/seeds}/participant.js +1 -1
  183. package/{seeds → src/seeds}/settlementInterchange.js +1 -0
  184. package/src/settlement/api/handlerRoutes.js +71 -0
  185. package/src/settlement/api/handlers/health.js +10 -0
  186. package/src/settlement/api/handlers/index.js +57 -0
  187. package/src/settlement/api/handlers/settlementWindows/{id}.js +103 -0
  188. package/src/settlement/api/handlers/settlementWindows.js +77 -0
  189. package/src/settlement/api/handlers/settlements/{id}.js +133 -0
  190. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}/accounts/{aid}.js +127 -0
  191. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}.js +127 -0
  192. package/src/settlement/api/handlers/settlements.js +114 -0
  193. package/src/settlement/api/openapiRouting.js +87 -0
  194. package/src/settlement/api/routes.js +176 -0
  195. package/src/settlement/domain/rules/index.js +46 -0
  196. package/src/settlement/domain/settlement/index.js +405 -0
  197. package/src/settlement/domain/settlementWindow/index.js +117 -0
  198. package/src/settlement/domain/transactions/index.js +83 -0
  199. package/src/settlement/domain/transferSettlement/index.js +57 -0
  200. package/src/settlement/handlers/deferredSettlement/handler.js +180 -0
  201. package/src/settlement/handlers/grossSettlement/handler.js +180 -0
  202. package/src/settlement/handlers/index.js +101 -0
  203. package/src/settlement/handlers/register.js +92 -0
  204. package/src/settlement/handlers/rules/handler.js +164 -0
  205. package/src/settlement/interface/swagger-handler.json +34 -0
  206. package/src/settlement/interface/swagger.json +1560 -0
  207. package/src/settlement/lib/db.js +32 -0
  208. package/src/settlement/lib/kafka/index.js +41 -0
  209. package/src/settlement/lib/kafka/producer.js +142 -0
  210. package/src/settlement/lib/scriptEngine.js +99 -0
  211. package/src/settlement/lib/scriptsLoader.js +175 -0
  212. package/src/settlement/lib/utility.js +243 -0
  213. package/src/settlement/models/ilpPackets/ilpPacket.js +42 -0
  214. package/src/settlement/models/lib/enums.js +192 -0
  215. package/src/settlement/models/misc/migrationLock.js +53 -0
  216. package/src/settlement/models/settlement/facade.js +1652 -0
  217. package/src/settlement/models/settlement/index.js +55 -0
  218. package/src/settlement/models/settlement/participantCurrency.js +48 -0
  219. package/src/settlement/models/settlement/settlement.js +49 -0
  220. package/src/settlement/models/settlement/settlementModel.js +44 -0
  221. package/src/settlement/models/settlement/settlementParticipantCurrency.js +61 -0
  222. package/src/settlement/models/settlement/settlementStateChange.js +44 -0
  223. package/src/settlement/models/settlement/settlementTransferParticipant.js +46 -0
  224. package/src/settlement/models/settlementWindow/facade.js +345 -0
  225. package/src/settlement/models/settlementWindow/index.js +47 -0
  226. package/src/settlement/models/settlementWindow/settlementWindowStateChange.js +55 -0
  227. package/src/settlement/models/settlementWindowContent/facade.js +79 -0
  228. package/src/settlement/models/settlementWindowContent/index.js +40 -0
  229. package/src/settlement/models/settlementWindowContent/settlementWindowContentStateChange.js +53 -0
  230. package/src/settlement/models/transferSettlement/facade.js +363 -0
  231. package/src/settlement/models/transferSettlement/index.js +42 -0
  232. package/src/settlement/shared/logger/index.js +7 -0
  233. package/src/settlement/shared/plugins.js +88 -0
  234. package/src/settlement/shared/setup.js +224 -0
  235. package/src/settlement/utils/truthyProperty.js +37 -0
  236. package/src/shared/legacyUtilShim.js +20 -0
  237. package/src/shared/plugins.js +3 -3
  238. package/src/shared/results.ts +22 -0
  239. package/src/shared/results.unit.ts +38 -0
  240. package/src/shared/setup.js +14 -1
  241. package/src/testing/api-helpers.ts +1222 -0
  242. package/src/testing/exec-async.ts +67 -0
  243. package/src/testing/harness.snapshot.sql +3895 -0
  244. package/src/testing/harness.test.ts +15 -0
  245. package/src/testing/harness.ts +1160 -0
  246. package/src/testing/kafka.ts +221 -0
  247. package/src/testing/prng.ts +82 -0
  248. package/src/testing/rdkafka.test.ts +263 -0
  249. package/src/testing/run.ts +559 -0
  250. package/src/testing/snapshot.test.ts +92 -0
  251. package/src/testing/snapshot.ts +549 -0
  252. package/src/testing/tap-stream.ts +164 -0
  253. package/src/testing/types.ts +95 -0
  254. package/src/testing/util.ts +333 -0
  255. package/src/types/central-services-error-handling.d.ts +72 -0
  256. package/src/types/central-services-stream.d.ts +148 -0
  257. package/test-integration.Dockerfile +0 -2
  258. package/test.Dockerfile +0 -2
  259. package/tsconfig.json +40 -0
  260. package/config/knexfile.js +0 -19
  261. package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +0 -63
  262. package/src/api/index.js +0 -16
  263. package/src/lib/config.js +0 -81
  264. /package/{migrations → src/migrations}/110101_contactType-indexes.js +0 -0
  265. /package/{migrations → src/migrations}/110301_endpointType-indexes.js +0 -0
  266. /package/{migrations → src/migrations}/110401_ledgerEntryType-indexes.js +0 -0
  267. /package/{migrations → src/migrations}/110451_ledgerAccountType-indexes.js +0 -0
  268. /package/{migrations → src/migrations}/110501_participantLimitType-indexes.js +0 -0
  269. /package/{migrations → src/migrations}/110601_transferParticipantRoleType-indexes.js +0 -0
  270. /package/{migrations → src/migrations}/110800_settlementWindowState.js +0 -0
  271. /package/{migrations → src/migrations}/111000_amountType.js +0 -0
  272. /package/{migrations → src/migrations}/111001_amountType-indexes.js +0 -0
  273. /package/{migrations → src/migrations}/111100_balanceOfPayments.js +0 -0
  274. /package/{migrations → src/migrations}/111101_balanceOfPayments-indexes.js +0 -0
  275. /package/{migrations → src/migrations}/111200_partyIdentifierType.js +0 -0
  276. /package/{migrations → src/migrations}/111201_partyIdentifierType-indexes.js +0 -0
  277. /package/{migrations → src/migrations}/111300_partyType.js +0 -0
  278. /package/{migrations → src/migrations}/111301_partyType-indexes.js +0 -0
  279. /package/{migrations → src/migrations}/111400_quoteDuplicateCheck.js +0 -0
  280. /package/{migrations → src/migrations}/111500_transactionInitiator.js +0 -0
  281. /package/{migrations → src/migrations}/111501_transactionInitiator-indexes.js +0 -0
  282. /package/{migrations → src/migrations}/111600_transactionInitiatorType.js +0 -0
  283. /package/{migrations → src/migrations}/111601_transactionInitiatorType-indexes.js +0 -0
  284. /package/{migrations → src/migrations}/111700_settlementDelay.js +0 -0
  285. /package/{migrations → src/migrations}/111701_settlementDelay-indexes.js +0 -0
  286. /package/{migrations → src/migrations}/111800_settlementInterchange.js +0 -0
  287. /package/{migrations → src/migrations}/111801_settlementInterchange-indexes.js +0 -0
  288. /package/{migrations → src/migrations}/111900_settlementGranularity.js +0 -0
  289. /package/{migrations → src/migrations}/111901_settlementGranularity-indexes.js +0 -0
  290. /package/{migrations → src/migrations}/112101_bulkProcessingState-indexes.js +0 -0
  291. /package/{migrations → src/migrations}/200101_participant-indexes.js +0 -0
  292. /package/{migrations → src/migrations}/200201_participantContact-indexes.js +0 -0
  293. /package/{migrations → src/migrations}/200301_participantEndpoint-indexes.js +0 -0
  294. /package/{migrations → src/migrations}/200401_participantParty-indexes.js +0 -0
  295. /package/{migrations → src/migrations}/300201_transfer-indexes.js +0 -0
  296. /package/{migrations → src/migrations}/300251_bulkTransfer-indexes.js +0 -0
  297. /package/{migrations → src/migrations}/300276_bulkTransferAssociation-indexes.js +0 -0
  298. /package/{migrations → src/migrations}/300401_transferStateChange-indexes.js +0 -0
  299. /package/{migrations → src/migrations}/300451_bulkTransferStateChange-indexes.js +0 -0
  300. /package/{migrations → src/migrations}/300501_segment-indexes.js +0 -0
  301. /package/{migrations → src/migrations}/310101_participantCurrency-indexes.js +0 -0
  302. /package/{migrations → src/migrations}/310201_transferParticipant-indexes.js +0 -0
  303. /package/{migrations → src/migrations}/310203_transferParticipant-indexes.js +0 -0
  304. /package/{migrations → src/migrations}/310301_participantPosition-indexes.js +0 -0
  305. /package/{migrations → src/migrations}/310401_participantPositionChange-indexes.js +0 -0
  306. /package/{migrations → src/migrations}/310405_participantPositionChange-positionChange.js +0 -0
  307. /package/{migrations → src/migrations}/310406_participantPositionChange-positionChange-revert.js +0 -0
  308. /package/{migrations → src/migrations}/310501_participantLimit-indexes.js +0 -0
  309. /package/{migrations → src/migrations}/310503_participantLimit-indexes-composite.js +0 -0
  310. /package/{migrations → src/migrations}/400401_settlmentWindowStateChange-indexes.js +0 -0
  311. /package/{migrations → src/migrations}/400501_settlementTransferParticipant-indexes.js +0 -0
  312. /package/{migrations → src/migrations}/400601_settlementParticipantCurrency-indexes.js +0 -0
  313. /package/{migrations → src/migrations}/400701_settlementParticipantCurrencyStateChange-indexes.js +0 -0
  314. /package/{migrations → src/migrations}/400801_settlementStateChange-indexes.js +0 -0
  315. /package/{migrations → src/migrations}/401002_settlementWindowContent-indexes.js +0 -0
  316. /package/{migrations → src/migrations}/401201_settlementContentAggregation-indexes.js +0 -0
  317. /package/{migrations → src/migrations}/401301_settlementModel-indexes.js +0 -0
  318. /package/{migrations → src/migrations}/410101_transferFulfilmentDuplicateCheck-indexes.js +0 -0
  319. /package/{migrations → src/migrations}/410201_transferFulfilment-indexes.js +0 -0
  320. /package/{migrations → src/migrations}/410301_transferExtension-indexes.js +0 -0
  321. /package/{migrations → src/migrations}/410351_bulkTransferExtension-indexes.js +0 -0
  322. /package/{migrations → src/migrations}/410401_transferTimeout-indexes.js +0 -0
  323. /package/{migrations → src/migrations}/410501_transferError-indexes.js +0 -0
  324. /package/{migrations → src/migrations}/410551_bulkTransferError-indexes.js +0 -0
  325. /package/{migrations → src/migrations}/410601_expiringTransfer-indexes.js +0 -0
  326. /package/{migrations → src/migrations}/500100_transactionReference.js +0 -0
  327. /package/{migrations → src/migrations}/500101_transactionReference-indexes.js +0 -0
  328. /package/{migrations → src/migrations}/500200_transactionScenario.js +0 -0
  329. /package/{migrations → src/migrations}/500201_transactionScenario-indexes.js +0 -0
  330. /package/{migrations → src/migrations}/500300_transactionSubScenario.js +0 -0
  331. /package/{migrations → src/migrations}/500400_quote.js +0 -0
  332. /package/{migrations → src/migrations}/500500_quoteParty.js +0 -0
  333. /package/{migrations → src/migrations}/500600_party.js +0 -0
  334. /package/{migrations → src/migrations}/500700_quoteResponse.js +0 -0
  335. /package/{migrations → src/migrations}/500800_quoteResponseIlpPacket.js +0 -0
  336. /package/{migrations → src/migrations}/500900_geoCode.js +0 -0
  337. /package/{migrations → src/migrations}/501000_quoteExtension.js +0 -0
  338. /package/{migrations → src/migrations}/501100_quoteResponseDuplicateCheck.js +0 -0
  339. /package/{migrations → src/migrations}/501200_quoteError.js +0 -0
  340. /package/{migrations → src/migrations}/501300_transferRules.js +0 -0
  341. /package/{migrations → src/migrations}/600011_fxTransferType-indexes.js +0 -0
  342. /package/{migrations → src/migrations}/600013_fxParticipantCurrencyType-indexes.js +0 -0
  343. /package/{migrations → src/migrations}/600201_fxTransfer-indexes.js +0 -0
  344. /package/{migrations → src/migrations}/600401_fxTransferStateChange-indexes.js +0 -0
  345. /package/{migrations → src/migrations}/600502_fxWatchList-indexes.js +0 -0
  346. /package/{migrations → src/migrations}/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +0 -0
  347. /package/{migrations → src/migrations}/600701_fxTransferFulfilment-indexes.js +0 -0
  348. /package/{migrations → src/migrations}/601401_fxTransferTimeout-indexes.js +0 -0
  349. /package/{migrations → src/migrations}/601501_fxTransferError-indexes.js +0 -0
  350. /package/{migrations → src/migrations}/610201_fxTransferParticipant-indexes.js +0 -0
  351. /package/{migrations → src/migrations}/910102_hotfix954.js +0 -0
  352. /package/{migrations → src/migrations}/940100_quotePartyIdInfoExtension.js +0 -0
  353. /package/{migrations → src/migrations}/950100_settlementContentAggregation-decimal184.js +0 -0
  354. /package/{migrations → src/migrations}/950106_unique_settlement_model_ledger_account_currency.js +0 -0
  355. /package/{migrations → src/migrations}/950109_fxQuote.js +0 -0
  356. /package/{migrations → src/migrations}/950110_fxQuoteResponse.js +0 -0
  357. /package/{migrations → src/migrations}/950111_fxQuoteError.js +0 -0
  358. /package/{migrations → src/migrations}/950113_fxQuoteDuplicateCheck.js +0 -0
  359. /package/{migrations → src/migrations}/950114_fxQuoteResponseDuplicateCheck.js +0 -0
  360. /package/{migrations → src/migrations}/950115_fxQuoteConversionTerms.js +0 -0
  361. /package/{migrations → src/migrations}/950116_fxQuoteConversionTermsExtension.js +0 -0
  362. /package/{migrations → src/migrations}/950117_fxQuoteResponseConversionTerms.js +0 -0
  363. /package/{migrations → src/migrations}/950118_fxQuoteResponseConversionTermsExtension.js +0 -0
  364. /package/{migrations → src/migrations}/950119_fxCharge.js +0 -0
  365. /package/{migrations → src/migrations}/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +0 -0
  366. /package/{migrations → src/migrations}/960112_fxTransfer-indexes2.js +0 -0
  367. /package/{migrations → src/migrations}/960113_fxTransferExtension-indexes.js +0 -0
  368. /package/{migrations → src/migrations}/960114_fxTransferTimeout-indexes2.js +0 -0
  369. /package/{migrations → src/migrations}/961000_participantEndpoint_unique.js +0 -0
  370. /package/{migrations → src/migrations}/migration.template +0 -0
  371. /package/{seeds → src/seeds}/amountType.js +0 -0
  372. /package/{seeds → src/seeds}/balanceOfPayments.js +0 -0
  373. /package/{seeds → src/seeds}/bulkProcessingState.js +0 -0
  374. /package/{seeds → src/seeds}/bulkTransferState.js +0 -0
  375. /package/{seeds → src/seeds}/currency.js +0 -0
  376. /package/{seeds → src/seeds}/endpointType.js +0 -0
  377. /package/{seeds → src/seeds}/fxParticipantCurrencyType.js +0 -0
  378. /package/{seeds → src/seeds}/fxTransferType.js +0 -0
  379. /package/{seeds → src/seeds}/ledgerAccountType.js +0 -0
  380. /package/{seeds → src/seeds}/ledgerEntryType.js +0 -0
  381. /package/{seeds → src/seeds}/participantLimitType.js +0 -0
  382. /package/{seeds → src/seeds}/partyIdentifierType.js +0 -0
  383. /package/{seeds → src/seeds}/partyType.js +0 -0
  384. /package/{seeds → src/seeds}/settlementDelay.js +0 -0
  385. /package/{seeds → src/seeds}/settlementGranularity.js +0 -0
  386. /package/{seeds → src/seeds}/settlementState.js +0 -0
  387. /package/{seeds → src/seeds}/settlementWindow1State.js +0 -0
  388. /package/{seeds → src/seeds}/settlementWindow2Open.js +0 -0
  389. /package/{seeds → src/seeds}/transactionInitiator.js +0 -0
  390. /package/{seeds → src/seeds}/transactionInitiatorType.js +0 -0
  391. /package/{seeds → src/seeds}/transactionScenario.js +0 -0
  392. /package/{seeds → src/seeds}/transferParticipantRoleType.js +0 -0
  393. /package/{seeds → src/seeds}/transferState.js +0 -0
  394. /package/{seeds → src/seeds}/z1000_settlementModel-deprecated.js- +0 -0
@@ -0,0 +1,538 @@
1
+ import { after, before, describe, it } from "node:test"
2
+ import assert from "node:assert"
3
+
4
+ import Harness from '../../testing/harness'
5
+ import { Snapshot } from "../../testing/snapshot"
6
+ import * as ApiHelpers from '../../testing/api-helpers'
7
+
8
+ import TimeoutHandler from '../timeouts/handler'
9
+ import TransferFacade from "../../models/transfer/facade"
10
+ import handlerAll from './handler'
11
+ import handlerPrepare from './prepare'
12
+ import { assertPositionDiff, sleepSeconds } from "../../testing/util"
13
+
14
+ const harness = Harness.getInstance()
15
+
16
+
17
+ describe('handlers/tranfers/handlers', () => {
18
+ before(async () => {
19
+ await harness.up()
20
+ await harness.setupGlobals()
21
+
22
+ // Register `topic-transfer-prepare` and `topic-transfer-fulfil` consumers. In most tests, we
23
+ // directly consume the prepare/fulfil, but still need to connect the consumer, since inside of
24
+ // prepare() and fulfil() we commit the kafka offsets.
25
+ await handlerAll.registerPrepareHandler()
26
+ await handlerAll.registerFulfilHandler()
27
+
28
+ // Create the hub accounts + settlement model.
29
+ const createHubPayload: ApiHelpers.CreateHubPayload = {
30
+ currencies: ['USD'],
31
+ settlementModels: [{
32
+ name: `DEFERRED_MULTILATERAL_NET_USD`,
33
+ settlementGranularity: "NET",
34
+ settlementInterchange: "MULTILATERAL",
35
+ settlementDelay: "DEFERRED",
36
+ currency: 'USD',
37
+ requireLiquidityCheck: true,
38
+ ledgerAccountType: "POSITION",
39
+ settlementAccountType: "SETTLEMENT",
40
+ autoPositionReset: true
41
+ }]
42
+ }
43
+ await ApiHelpers.createHub(harness, createHubPayload)
44
+ // Create 2 test dfsps to transfer between.
45
+ await ApiHelpers.createDfsp(harness, {
46
+ name: 'dfsp_a',
47
+ currencies: ['USD'],
48
+ isProxy: false,
49
+ initialPostionsAndLimits: [
50
+ {
51
+ initialPosition: 0,
52
+ value: 100000
53
+ }
54
+ ],
55
+ deposits: [10000]
56
+ })
57
+ await ApiHelpers.createDfsp(harness, {
58
+ name: 'dfsp_b',
59
+ currencies: ['USD'],
60
+ isProxy: false,
61
+ initialPostionsAndLimits: [
62
+ {
63
+ initialPosition: 0,
64
+ value: 100000
65
+ }
66
+ ],
67
+ deposits: [10000]
68
+ })
69
+ })
70
+
71
+ after(async () => {
72
+ await harness.teardownGlobals()
73
+ await harness.down()
74
+ })
75
+
76
+ it('prepare() prepares a payment from dfsp_a -> dfsp_b.', async () => {
77
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
78
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
79
+ await ApiHelpers.buildPayment()
80
+ .deps(harness, handlerAll)
81
+ .parties('dfsp_a', 'dfsp_b')
82
+ .transferId('1000001')
83
+ .build()
84
+ .prepare()
85
+
86
+ // Get the transfer:
87
+ const transfer = await TransferFacade.getById('1000001')
88
+ Snapshot.from(`{
89
+ "transferId": "1000001",
90
+ "amount": "100.0000",
91
+ "currencyId": "USD",
92
+ "ilpCondition": :ignore
93
+ "expirationDate": :ignore
94
+ "createdDate": :ignore
95
+ "currency": "USD",
96
+ "payerParticipantCurrencyId": 3,
97
+ "payerAmount": "100.0000",
98
+ "payerParticipantId": 2,
99
+ "payerFsp": "dfsp_a",
100
+ "payerIsProxy": 0,
101
+ "payeeParticipantCurrencyId": null,
102
+ "payeeAmount": "-100.0000",
103
+ "payeeParticipantId": 3,
104
+ "payeeFsp": "dfsp_b",
105
+ "payeeIsProxy": 0,
106
+ "transferStateChangeId": 10,
107
+ "transferState": "RESERVED",
108
+ "reason": null,
109
+ "completedTimestamp": :ignore
110
+ "transferStateEnumeration": "RESERVED",
111
+ "transferStateDescription": "The switch has reserved the transfer.",
112
+ "ilpPacket": :ignore
113
+ "condition": :ignore
114
+ "fulfilment": null,
115
+ "errorCode": null,
116
+ "errorDescription": null,
117
+ "externalPayerName": null,
118
+ "externalPayeeName": null,
119
+ "extensionList": [],
120
+ "isTransferReadModel": true
121
+ }`).checkUnwrap(transfer)
122
+
123
+ // Check the position changes.
124
+ let positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
125
+ let positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
126
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 100 })
127
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
128
+
129
+ // Check the last 2 message types.
130
+ const topics = harness.spoolLastTopic(2)
131
+ Snapshot.from(`[
132
+ "topic-transfer-position",
133
+ "topic-notification-event"
134
+ ]`).checkUnwrap(topics)
135
+ })
136
+
137
+ it('prepare() + fulfil() + COMMIT completes a payment.', async () => {
138
+ const transferId = '1000002'
139
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
140
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
141
+
142
+ const payment = await ApiHelpers.buildPayment()
143
+ .deps(harness, handlerAll)
144
+ .parties('dfsp_a', 'dfsp_b')
145
+ .transferId(transferId)
146
+ .build()
147
+ .prepare()
148
+
149
+ let positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
150
+ let positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
151
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 100 })
152
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
153
+
154
+ await payment.fulfil()
155
+
156
+ positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
157
+ positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
158
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { posted: 100 })
159
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { posted: -100 })
160
+
161
+ // Get the transfer:
162
+ const transfer = await TransferFacade.getById(transferId)
163
+ Snapshot.from(`{
164
+ "transferId": "1000002",
165
+ "amount": "100.0000",
166
+ "currencyId": "USD",
167
+ "ilpCondition": :ignore
168
+ "expirationDate": :ignore
169
+ "createdDate": :ignore
170
+ "currency": "USD",
171
+ "payerParticipantCurrencyId": 3,
172
+ "payerAmount": "100.0000",
173
+ "payerParticipantId": 2,
174
+ "payerFsp": "dfsp_a",
175
+ "payerIsProxy": 0,
176
+ "payeeParticipantCurrencyId": null,
177
+ "payeeAmount": "-100.0000",
178
+ "payeeParticipantId": 3,
179
+ "payeeFsp": "dfsp_b",
180
+ "payeeIsProxy": 0,
181
+ "transferStateChangeId": :ignore,
182
+ "transferState": "COMMITTED",
183
+ "reason": null,
184
+ "completedTimestamp": :ignore
185
+ "transferStateEnumeration": "COMMITTED",
186
+ "transferStateDescription": "The switch has successfully performed the transfer.",
187
+ "ilpPacket": :ignore
188
+ "condition": :ignore
189
+ "fulfilment": ":ignore",
190
+ "errorCode": null,
191
+ "errorDescription": null,
192
+ "externalPayerName": null,
193
+ "externalPayeeName": null,
194
+ "extensionList": [],
195
+ "isTransferReadModel": true
196
+ }`).checkUnwrap(transfer)
197
+
198
+ // Check the last 2 topics.
199
+ const topics = harness.spoolLastTopic(2)
200
+ Snapshot.from(`[
201
+ "topic-transfer-position",
202
+ "topic-notification-event"
203
+ ]`).checkUnwrap(topics)
204
+ })
205
+
206
+ it('fulfil() + ABORT rejects a payment.', async () => {
207
+ // B -> A
208
+ const transferId = '1000003'
209
+ const payment = await ApiHelpers.buildPayment()
210
+ .deps(harness, handlerAll)
211
+ .parties('dfsp_b', 'dfsp_a')
212
+ .transferId(transferId)
213
+ .amount('50.00')
214
+ .build()
215
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
216
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
217
+
218
+ await payment.prepare()
219
+
220
+ let positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
221
+ let positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
222
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { posted: 50 })
223
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { posted: 0 })
224
+
225
+ await payment.abort()
226
+
227
+ // Get the transfer:
228
+ const transfer = await TransferFacade.getById(transferId)
229
+ assert.equal(transfer.transferState, 'ABORTED_ERROR')
230
+
231
+ // Position should have reverted.
232
+ positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
233
+ positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
234
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 0 })
235
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
236
+
237
+ // Check the last 2 message types.
238
+ const topics = harness.spoolLastTopic(2)
239
+ Snapshot.from(`[
240
+ "topic-transfer-position",
241
+ "topic-notification-event"
242
+ ]`).checkUnwrap(topics)
243
+ })
244
+
245
+ it('prepare() handles a reused transferId (different body)', async () => {
246
+ const transferId = '1000004'
247
+ const now = new Date()
248
+ const paymentA = await ApiHelpers.buildPayment()
249
+ .deps(harness, handlerAll)
250
+ .parties('dfsp_a', 'dfsp_b')
251
+ .transferId(transferId)
252
+ .amount('10.00')
253
+ .date(now)
254
+ .build()
255
+ const paymentB = await ApiHelpers.buildPayment()
256
+ .deps(harness, handlerAll)
257
+ .parties('dfsp_a', 'dfsp_b')
258
+ .transferId(transferId)
259
+ .amount('12.00') // Changed amount.
260
+ .date(now)
261
+ .build()
262
+
263
+ // Prepare the first payment.
264
+ await paymentA.prepare()
265
+
266
+ // Manually prepare the second payment - it will fail.
267
+ const mark = harness.redpandaMark()
268
+ await handlerPrepare.prepare(null, [paymentB.buildMessagePrepare()])
269
+ await harness.redpandaDrain(mark, 1)
270
+
271
+ const messages = harness.spoolLastPayload(1)
272
+ Snapshot.from(`[
273
+ {
274
+ "errorInformation": {
275
+ "errorCode": "3106",
276
+ "errorDescription": "Modified request",
277
+ "extensionList": {
278
+ "extension": [
279
+ {
280
+ "key": "cause",
281
+ "value": :ignore
282
+ }
283
+ ]
284
+ }
285
+ }
286
+ }
287
+ ]`).checkUnwrap(messages)
288
+ })
289
+
290
+ it('fulfil() + RESERVE completes a payment.', async () => {
291
+ const transferId = '1000005'
292
+ const payment = ApiHelpers.buildPayment()
293
+ .deps(harness, handlerAll)
294
+ .parties('dfsp_a', 'dfsp_b')
295
+ .transferId(transferId)
296
+ .amount('14.00')
297
+ .build()
298
+
299
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
300
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
301
+
302
+ await payment.prepare()
303
+ await payment.fulfil('RESERVED')
304
+
305
+ // Get the transfer:
306
+ const transfer = await TransferFacade.getById(transferId)
307
+ assert.equal(transfer.transferState, 'COMMITTED')
308
+
309
+ // Check the position.
310
+ const positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
311
+ const positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
312
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 14 })
313
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: -14 })
314
+
315
+ // Check the last messages.
316
+ const topics = harness.spoolLastTopic(2)
317
+ Snapshot.from(`[
318
+ "topic-transfer-position",
319
+ "topic-notification-event"
320
+ ]`).checkUnwrap(topics)
321
+ })
322
+
323
+ it('fulfil() + COMMIT with an invalid fulfilment.', async () => {
324
+ const transferId = '1000006'
325
+ const amount = '14.00'
326
+ let now = new Date();
327
+ const payment = ApiHelpers.buildPayment()
328
+ .deps(harness, handlerAll)
329
+ .parties('dfsp_a', 'dfsp_b')
330
+ .transferId(transferId)
331
+ .amount(amount)
332
+ .build()
333
+
334
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
335
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
336
+
337
+ await payment.prepare()
338
+
339
+ const putTransfer = ApiHelpers.buildMojaloopPutTransfer({
340
+ payerFsp: 'dfsp_a',
341
+ payeeFsp: 'dfsp_b',
342
+ transferId,
343
+ amountComplex: {
344
+ amount,
345
+ currency: 'KES' // This will cause the fulfilment to be invalid.
346
+ },
347
+ date: now,
348
+ expirySeconds: 100,
349
+ transferState: "RESERVED"
350
+ })
351
+
352
+ const mark = harness.redpandaMark()
353
+ await handlerAll.fulfil(null, [ApiHelpers.buildMessageFulfil(harness, putTransfer, transferId)])
354
+ await harness.redpandaDrain(mark, 2)
355
+
356
+ // Get the transfer:
357
+ const transfer = await TransferFacade.getById(transferId)
358
+ assert.equal(transfer.transferState, 'ABORTED_ERROR')
359
+
360
+ // Check the position.
361
+ const positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
362
+ const positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
363
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 0 })
364
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
365
+
366
+ // Check the last messages.
367
+ const topics = harness.spoolLastTopic(2)
368
+ Snapshot.from(`[
369
+ "topic-transfer-position",
370
+ "topic-notification-event"
371
+ ]`).checkUnwrap(topics)
372
+ })
373
+
374
+ it('Payer dfsp cannot fulfill their own payment.', async () => {
375
+ const transferId = '1000007'
376
+ const amount = '14.00'
377
+ let now = new Date();
378
+ const payment = ApiHelpers.buildPayment()
379
+ .deps(harness, handlerAll)
380
+ .parties('dfsp_a', 'dfsp_b')
381
+ .transferId(transferId)
382
+ .amount(amount)
383
+ .build()
384
+
385
+ await payment.prepare()
386
+
387
+ const putTransfer = ApiHelpers.buildMojaloopPutTransfer({
388
+ payerFsp: 'dfsp_a',
389
+ payeeFsp: 'dfsp_b',
390
+ transferId,
391
+ amountComplex: {
392
+ amount,
393
+ currency: 'USD'
394
+ },
395
+ date: now,
396
+ expirySeconds: 100,
397
+ transferState: "RESERVED"
398
+ })
399
+
400
+ // Flip the headers, so it comes from the payer.
401
+ putTransfer.headers['fspiop-source'] = 'dfsp_a'
402
+ putTransfer.headers['fspiop-destination'] = 'dfsp_b'
403
+
404
+ const mark = harness.redpandaMark()
405
+ await handlerAll.fulfil(null, [ApiHelpers.buildMessageFulfil(harness, putTransfer, transferId)])
406
+ await harness.redpandaDrain(mark, 2)
407
+
408
+ // Get the transfer:
409
+ const transfer = await TransferFacade.getById(transferId)
410
+ assert.equal(transfer.transferState, 'ABORTED_ERROR')
411
+ assert.equal(
412
+ transfer.reason,
413
+ 'Generic validation error - fspiop-destination does not match payer fsp on the Fulfil callback response'
414
+ )
415
+ })
416
+
417
+ it('3rd dfsp cannot fulfill a payment.', async () => {
418
+ const transferId = '1000017'
419
+ const amount = '14.00'
420
+ let now = new Date();
421
+ const postTransfer = ApiHelpers.buildMojaloopPostTransfer({
422
+ payerFsp: 'dfsp_a',
423
+ payeeFsp: 'dfsp_b',
424
+ transferId,
425
+ amountComplex: {
426
+ amount,
427
+ currency: 'USD'
428
+ },
429
+ date: now,
430
+ expirySeconds: 100
431
+ })
432
+
433
+ let mark = harness.redpandaMark()
434
+ await handlerPrepare.prepare(null, [ApiHelpers.buildMessagePrepare(harness, postTransfer)])
435
+ await harness.redpandaDrain(mark, 2)
436
+
437
+ const putTransfer = ApiHelpers.buildMojaloopPutTransfer({
438
+ payerFsp: 'dfsp_a',
439
+ payeeFsp: 'dfsp_b',
440
+ transferId,
441
+ amountComplex: {
442
+ amount,
443
+ currency: 'USD'
444
+ },
445
+ date: now,
446
+ expirySeconds: 100,
447
+ transferState: "RESERVED"
448
+ })
449
+
450
+ // Make the callback come from a 3rd dfsp.
451
+ putTransfer.headers['fspiop-source'] = 'dfsp_x'
452
+
453
+ mark = harness.redpandaMark()
454
+ await handlerAll.fulfil(null, [ApiHelpers.buildMessageFulfil(harness, putTransfer, transferId)])
455
+ await harness.redpandaDrain(mark, 2)
456
+
457
+ // Check the last messages.
458
+ const topics = harness.spoolLastTopic(2)
459
+ Snapshot.from(`[
460
+ "topic-transfer-position",
461
+ "topic-notification-event"
462
+ ]`).checkUnwrap(topics)
463
+
464
+ // Get the transfer:
465
+ const transfer = await TransferFacade.getById(transferId)
466
+ assert.equal(transfer.transferState, 'ABORTED_ERROR')
467
+ assert.equal(
468
+ transfer.reason,
469
+ 'Generic validation error - fspiop-source does not match payee fsp on the Fulfil callback response'
470
+ )
471
+ })
472
+
473
+ it('prepare() + wait times out a transfer.', async () => {
474
+ const transferId = '1000008'
475
+ const payment = ApiHelpers.buildPayment()
476
+ .deps(harness, handlerAll)
477
+ .parties('dfsp_a', 'dfsp_b')
478
+ .transferId(transferId)
479
+ .amount('14.00')
480
+ .expiry(1)
481
+ .build()
482
+
483
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
484
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
485
+
486
+ await payment.prepare()
487
+ await sleepSeconds(10)
488
+ // We don't run the timeout handler in these tests, but instead just call timeout()
489
+ // directly. This makes the tests more predictable and deterministic.
490
+ const mark = harness.redpandaMark()
491
+ const timeoutResult = (await TimeoutHandler.timeout()) as unknown as {transferTimeoutList: Array<unknown>}
492
+ assert(timeoutResult.transferTimeoutList.length === 1)
493
+ await harness.redpandaDrain(mark, 2)
494
+
495
+ const lastTopics = harness.spoolLastTopic(2)
496
+ Snapshot.from(`[
497
+ "topic-transfer-position",
498
+ "topic-notification-event"
499
+ ]`).checkUnwrap(lastTopics)
500
+
501
+ // Positions should have reset with timeout.
502
+ const positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
503
+ const positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
504
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 0 })
505
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
506
+
507
+ // Check the transfer.
508
+ const transfer = await TransferFacade.getById(transferId)
509
+ assert.equal(transfer.transferState, 'EXPIRED_RESERVED')
510
+ assert.equal(transfer.reason, 'Transfer expired')
511
+ })
512
+
513
+ it('prepare() handles payment which exceeds Payer position.', async () => {
514
+ const transferId = '1000009'
515
+ const payment = ApiHelpers.buildPayment()
516
+ .deps(harness, handlerAll)
517
+ .parties('dfsp_a', 'dfsp_b')
518
+ .transferId(transferId)
519
+ .amount('10000000.00')
520
+ .build()
521
+
522
+ const positionPayerStart = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
523
+ const positionPayeeStart = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
524
+
525
+ await payment.prepare()
526
+
527
+ // Check the position changes.
528
+ let positionPayerEnd = await ApiHelpers.getPositionAccount('dfsp_a', 'USD')
529
+ let positionPayeeEnd = await ApiHelpers.getPositionAccount('dfsp_b', 'USD')
530
+ assertPositionDiff('payer', positionPayerStart, positionPayerEnd, { pending: 0, posted: 0 })
531
+ assertPositionDiff('payee', positionPayeeStart, positionPayeeEnd, { pending: 0, posted: 0 })
532
+
533
+ // Check the transfer state.
534
+ const transfer = await TransferFacade.getById(transferId)
535
+ assert.equal(transfer.transferState, 'ABORTED_REJECTED')
536
+ assert.equal(transfer.reason, 'Payer FSP insufficient liquidity')
537
+ })
538
+ })