@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,1222 @@
1
+ import { Enum, LedgerAccountTypeEnum } from '@mojaloop/central-services-shared'
2
+ import Harness from './harness'
3
+
4
+ import ParticipantService from '../domain/participant/index'
5
+ import SettlementModelService from '../domain/settlement'
6
+
7
+ import assert from "node:assert"
8
+
9
+ import Logger from "@mojaloop/central-services-logger"
10
+ import { Snapshot } from './snapshot'
11
+ import { sleepSeconds } from './util'
12
+
13
+ const { ilpFactory, ILP_VERSIONS } = require('@mojaloop/sdk-standard-components').Ilp
14
+ const ilpService = ilpFactory(ILP_VERSIONS.v1, { secret: 'password', logger: Logger })
15
+
16
+ export interface CreateSettlementModelPayload {
17
+ name: string
18
+ settlementGranularity: string
19
+ settlementInterchange: string
20
+ settlementDelay: string
21
+ currency: string
22
+ requireLiquidityCheck: boolean
23
+ ledgerAccountType: string
24
+ settlementAccountType: string
25
+ autoPositionReset: boolean
26
+ }
27
+
28
+ export interface CreateHubPayload {
29
+ currencies: Array<string>
30
+ settlementModels: Array<CreateSettlementModelPayload>
31
+ }
32
+
33
+ /**
34
+ * Creates hub accounts and settlement models.
35
+ */
36
+ export const createHub = async (harness: Harness, payload: CreateHubPayload): Promise<void> => {
37
+ assert.equal(payload.currencies.length, payload.settlementModels.length)
38
+ for (const currency of payload.currencies) {
39
+ await ParticipantService.createHubAccount(
40
+ harness.config.HUB_ID, currency, Enum.Accounts.LedgerAccountType.HUB_RECONCILIATION
41
+ )
42
+ await ParticipantService.createHubAccount(
43
+ harness.config.HUB_ID, currency, Enum.Accounts.LedgerAccountType.HUB_MULTILATERAL_SETTLEMENT
44
+ )
45
+ }
46
+
47
+ for (const settlementModel of payload.settlementModels) {
48
+ await SettlementModelService.createSettlementModel(settlementModel)
49
+ }
50
+ }
51
+
52
+ export type CreateDfspPayload = {
53
+ name: string,
54
+ currencies: Array<string>,
55
+ initialPostionsAndLimits: Array<{
56
+ value: number,
57
+ initialPosition: number
58
+ }>,
59
+ deposits: Array<number>
60
+ isProxy: boolean
61
+ }
62
+
63
+ /**
64
+ * Creates a DFSP.
65
+ */
66
+ export const createDfsp = async (harness: Harness, payload: CreateDfspPayload): Promise<void> => {
67
+ const accountTypes: Array<LedgerAccountTypeEnum> = [
68
+ Enum.Accounts.LedgerAccountType.POSITION,
69
+ Enum.Accounts.LedgerAccountType.SETTLEMENT
70
+ ]
71
+ const { name, currencies, isProxy, initialPostionsAndLimits, deposits } = payload;
72
+ assert.equal(currencies.length, initialPostionsAndLimits.length)
73
+ assert.equal(currencies.length, deposits.length)
74
+
75
+ const resultGetByName = await ParticipantService.getByName(name)
76
+ assert.equal(resultGetByName, undefined, `dfsp with name: ${name} already exists.`)
77
+
78
+ const participantId = await ParticipantService.create({
79
+ name, isProxy,
80
+ })
81
+ for (const currency of currencies) {
82
+ for (const accountType of accountTypes) {
83
+ await ParticipantService.createParticipantCurrency(
84
+ participantId,
85
+ currency,
86
+ accountType,
87
+ true
88
+ )
89
+ }
90
+ }
91
+
92
+ for (const [idx, limit] of initialPostionsAndLimits.entries()) {
93
+ const currency = currencies[idx]
94
+ const payload = {
95
+ currency,
96
+ limit: {
97
+ type: 'NET_DEBIT_CAP',
98
+ value: limit.value
99
+ },
100
+ initialPosition: limit.initialPosition
101
+ }
102
+ const mark = harness.redpandaMark()
103
+ let result = await ParticipantService.addLimitAndInitialPosition(name, payload)
104
+ assert.equal(result, true)
105
+ await harness.redpandaDrain(mark, 1)
106
+
107
+ result = await ParticipantService.getPositions(name, { currency })
108
+ Snapshot.from(`{
109
+ "currency": "${currency}",
110
+ "value": "${limit.initialPosition}.0000",
111
+ "changedDate": ":ignore"
112
+ }`).checkUnwrap(result)
113
+ }
114
+
115
+ for await (const [idx, currency] of currencies.entries()) {
116
+ const deposit = deposits[idx]
117
+ const payload = {
118
+ action: Enum.Events.Event.Action.RECORD_FUNDS_IN,
119
+ reason: 'deposit',
120
+ externalReference: `deposit-${name}`,
121
+ amount: {
122
+ amount: deposit.toString(),
123
+ currency,
124
+ }
125
+ };
126
+ let accounts = await ParticipantService.getAccounts(name, { currency })
127
+ let settlementAccount = await ParticipantService.getAccountByNameAndCurrency(
128
+ name, currency, Enum.Accounts.LedgerAccountType.SETTLEMENT
129
+ );
130
+ assert(settlementAccount, 'Settlement account not found');
131
+
132
+ const mark = harness.redpandaMark()
133
+ await ParticipantService.recordFundsInOut(
134
+ payload,
135
+ { name, id: settlementAccount.participantCurrencyId, transferId: `${name}_${currency}_01` },
136
+ harness.enums
137
+ )
138
+ await harness.redpandaDrain(mark, 1)
139
+
140
+ // Annoyingly we still have a position update race condition here.
141
+ await sleepSeconds(2)
142
+ accounts = await ParticipantService.getAccounts(name, { currency })
143
+ const settlementAccountResponse = accounts
144
+ .filter((account: any) => account.ledgerAccountType === 'SETTLEMENT')[0]
145
+ assert(settlementAccountResponse)
146
+ Snapshot.from(`{
147
+ "id": :int,
148
+ "ledgerAccountType": "SETTLEMENT",
149
+ "currency": "${currency}",
150
+ "isActive": 1,
151
+ "value": "-${deposit}.0000",
152
+ "reservedValue": "0.0000",
153
+ "changedDate": :ignore
154
+ }`).checkUnwrap(settlementAccountResponse)
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Helper to get dfsp positions.
160
+ */
161
+ export const getPositions = async (payer: string, payee: string, currency: string = 'USD') => {
162
+ return Promise.all([getPositionAccount(payer, currency), getPositionAccount(payee, currency)])
163
+ }
164
+
165
+ /**
166
+ * Helper to get Position account.
167
+ */
168
+ export const getPositionAccount = async (name: string, currency: string) => {
169
+ const account = (await ParticipantService.getAccounts(name, { currency }))
170
+ .find(account => account.ledgerAccountType === 'POSITION')
171
+
172
+ assert(account, `No position account found for name: ${name} + currency: ${currency}.`)
173
+ return account;
174
+ }
175
+
176
+ /**
177
+ * Helper to build both valid and invalid Mojaloop Payments.
178
+ * @example
179
+ * // Create payment of $100.00 USD from dfsp_a to dfsp_b with id 1000001.
180
+ * const payment = ApiHelpers.buildPayment()
181
+ * .deps(harness, TransferHandler)
182
+ * .parties('dfsp_a', 'dfsp_b')
183
+ * .transferId('1000001')
184
+ * .build()
185
+ *
186
+ * // Prepare the the payment.
187
+ * await payment.prepare()
188
+ *
189
+ * // Fulfil the payment.
190
+ * await payment.fulfil()
191
+ *
192
+ */
193
+ export function buildPayment(): PaymentBuilder {
194
+ return new PaymentBuilder()
195
+ }
196
+
197
+ /**
198
+ * Helper to build Forex Payments. (POST /fxTransfer, PUT /fxTransfer).
199
+ *
200
+ * @example
201
+ * const forex = ApiHelpers.buildForex()
202
+ * .deps(harness, TransferHandler)
203
+ * .commitRequestId('2000002')
204
+ * .determiningTransferId('3000002')
205
+ * .parties('external_dfsp_a', 'external_dfsp_b')
206
+ * .amountSource('100.00', 'BWP')
207
+ * .amountTarget('1.00', 'USD')
208
+ * .build()
209
+ *
210
+ * // Prepare the forex.
211
+ * await forex.prepare()
212
+ */
213
+ export function buildForex(): ForexBuilder {
214
+ return new ForexBuilder()
215
+ }
216
+
217
+ export interface PaymentOptions {
218
+ harness: Harness,
219
+ transferHandler: {
220
+ prepare: (error: any, message: any) => Promise<void>
221
+ fulfil: (error: any, message: any) => Promise<void>
222
+ }
223
+ payerFsp: string,
224
+ payeeFsp: string,
225
+ transferId: string,
226
+ amountComplex: {
227
+ amount: string,
228
+ currency: string,
229
+ }
230
+ date: Date,
231
+ expirySeconds: number,
232
+ fx: boolean
233
+ }
234
+
235
+ export class Payment {
236
+ private options: PaymentOptions;
237
+
238
+ public constructor(options: PaymentOptions) {
239
+ this.options = options
240
+ }
241
+
242
+ public async prepare(): Promise<this> {
243
+ const mark = this.options.harness.redpandaMark()
244
+ this.options.transferHandler.prepare(null, this.buildMessagePrepare())
245
+ await this.options.harness.redpandaDrain(mark, 2)
246
+
247
+ return this
248
+ }
249
+
250
+ /**
251
+ * Build the prepare message to be passed to the kafka prepare handler.
252
+ */
253
+ public buildMessagePrepare() {
254
+ const params: PostTransferBuilderParams = {
255
+ payerFsp: this.options.payerFsp,
256
+ payeeFsp: this.options.payeeFsp,
257
+ transferId: this.options.transferId,
258
+ amountComplex: {
259
+ amount: this.options.amountComplex.amount,
260
+ currency: this.options.amountComplex.currency,
261
+ },
262
+ date: this.options.date,
263
+ expirySeconds: this.options.expirySeconds
264
+ }
265
+ const postTransfer = buildMojaloopPostTransfer(params)
266
+ const messageKafka = buildMessagePrepare(this.options.harness, postTransfer)
267
+
268
+ return messageKafka
269
+ }
270
+
271
+ /**
272
+ * Build the fulfil message to be passed to the kafka fulfil handler.
273
+ */
274
+ public buildMessageFulfil(state: 'COMMITTED' | 'RESERVED') {
275
+ const params: PutTransferBuilderParams = {
276
+ payerFsp: this.options.payerFsp,
277
+ payeeFsp: this.options.payeeFsp,
278
+ transferId: this.options.transferId,
279
+ amountComplex: {
280
+ amount: this.options.amountComplex.amount,
281
+ currency: this.options.amountComplex.currency,
282
+ },
283
+ date: this.options.date,
284
+ expirySeconds: this.options.expirySeconds,
285
+ transferState: state
286
+ }
287
+ const putTransfer = buildMojaloopPutTransfer(params)
288
+ const messageKafka = buildMessageFulfil(
289
+ this.options.harness, putTransfer, this.options.transferId
290
+ )
291
+
292
+ return messageKafka
293
+ }
294
+
295
+ public buildMessageAbort() {
296
+ const putTransfer = buildMojaloopAbortTransfer({
297
+ payerFsp: this.options.payerFsp,
298
+ payeeFsp: this.options.payeeFsp,
299
+ date: this.options.date,
300
+ })
301
+ const messageKafka = buildMessageAbort(
302
+ this.options.harness, putTransfer, this.options.transferId
303
+ )
304
+
305
+ return messageKafka
306
+ }
307
+
308
+ public async fulfil(state: 'COMMITTED' | 'RESERVED' = 'COMMITTED'): Promise<this> {
309
+ const mark = this.options.harness.redpandaMark()
310
+ await this.options.transferHandler.fulfil(null, this.buildMessageFulfil(state))
311
+ if (this.options.fx) {
312
+ // If this is a properly build fx transfer, we want to consume 4 messages.
313
+ await this.options.harness.redpandaDrain(mark, 4)
314
+ return this
315
+ }
316
+
317
+ await this.options.harness.redpandaDrain(mark, 2)
318
+
319
+ return this
320
+ }
321
+
322
+ public async prepareAndFulfil(): Promise<this> {
323
+ await this.prepare()
324
+ await this.fulfil()
325
+
326
+ return this
327
+ }
328
+
329
+ public async abort(): Promise<this> {
330
+ const mark = this.options.harness.redpandaMark()
331
+ await this.options.transferHandler.fulfil(null, this.buildMessageAbort())
332
+ await this.options.harness.redpandaDrain(mark, 2)
333
+
334
+ return this
335
+ }
336
+ }
337
+
338
+
339
+ export class PaymentBuilder {
340
+ private harness!: Harness
341
+ private transferHandler!: {
342
+ prepare: (error: any, message: any) => Promise<any>
343
+ fulfil: (error: any, message: any) => Promise<any>
344
+ }
345
+ private payerFsp!: string
346
+ private payeeFsp!: string
347
+ private _transferId!: string
348
+ private _date: Date = new Date();
349
+ private expirySeconds: number = 30;
350
+ private _fx: boolean = false
351
+ private amountComplex: {
352
+ amount: string,
353
+ currency: string
354
+ } = { amount: '100.00', currency: 'USD' };
355
+
356
+ deps(harness: Harness, transferHandler: {
357
+ prepare: (error: any, message: any) => Promise<any>,
358
+ fulfil: (error: any, message: any) => Promise<any>,
359
+ }): this {
360
+ this.harness = harness
361
+ this.transferHandler = transferHandler
362
+
363
+ return this
364
+ }
365
+
366
+ parties(payerFsp: string, payeeFsp: string): this {
367
+ this.payerFsp = payerFsp
368
+ this.payeeFsp = payeeFsp
369
+
370
+ return this
371
+ }
372
+
373
+ transferId(transferId: string): this {
374
+ this._transferId = transferId
375
+
376
+ return this
377
+ }
378
+
379
+ date(date: Date): this {
380
+ this._date = date
381
+
382
+ return this
383
+ }
384
+
385
+ expiry(seconds: number): this {
386
+ this.expirySeconds = seconds;
387
+
388
+ return this;
389
+ }
390
+
391
+ fx(fx: boolean = true): this {
392
+ this._fx = fx
393
+
394
+ return this
395
+ }
396
+
397
+ amount(amount: string, currency: string = 'USD') {
398
+ this.amountComplex = {
399
+ amount,
400
+ currency
401
+ }
402
+
403
+ return this
404
+ }
405
+
406
+ build(): Payment {
407
+ assert(this.harness)
408
+ assert(this.transferHandler)
409
+
410
+ const options: PaymentOptions = {
411
+ harness: this.harness,
412
+ transferHandler: this.transferHandler,
413
+ payerFsp: this.payerFsp,
414
+ payeeFsp: this.payeeFsp,
415
+ transferId: this._transferId,
416
+ amountComplex: this.amountComplex,
417
+ date: this._date,
418
+ expirySeconds: this.expirySeconds,
419
+ fx: this._fx,
420
+ }
421
+
422
+ return new Payment(options)
423
+ }
424
+ }
425
+
426
+ export interface ForexOptions {
427
+ harness: Harness,
428
+ transferHandler: {
429
+ prepare: (error: any, message: any) => Promise<void>
430
+ fulfil: (error: any, message: any) => Promise<void>
431
+ }
432
+ commitRequestId: string,
433
+ determiningTransferId: string,
434
+ initiatingFsp: string,
435
+ counterPartyFsp: string,
436
+ amountType: 'SEND' | 'RECEIVE',
437
+ sourceAmountComplex: { amount: string, currency: string }
438
+ targetAmountComplex: { amount: string, currency: string }
439
+ date: Date,
440
+ expirySeconds: number
441
+ }
442
+
443
+ export class Forex {
444
+ public constructor(private options: ForexOptions) { }
445
+
446
+ public async prepare(): Promise<this> {
447
+ const mark = this.options.harness.redpandaMark()
448
+ await this.options.transferHandler.prepare(null, this.buildMessagePrepare())
449
+ await this.options.harness.redpandaDrain(mark, 2)
450
+
451
+ return this
452
+ }
453
+
454
+ /**
455
+ * Build the prepare message to be passed to the kafka prepare handler.
456
+ * Public api so we can modify it to generate invalid messages.
457
+ */
458
+ public buildMessagePrepare() {
459
+ const params: PostFxTransferBuilderParams = {
460
+ commitRequestId: this.options.commitRequestId,
461
+ determiningTransferId: this.options.determiningTransferId,
462
+ initiatingFsp: this.options.initiatingFsp,
463
+ counterPartyFsp: this.options.counterPartyFsp,
464
+ amountType: this.options.amountType,
465
+ sourceAmount: {
466
+ amount: this.options.sourceAmountComplex.amount,
467
+ currency: this.options.sourceAmountComplex.currency,
468
+ },
469
+ targetAmount: {
470
+ amount: this.options.targetAmountComplex.amount,
471
+ currency: this.options.targetAmountComplex.currency
472
+ },
473
+ condition: '8x04dj-RKEtfjStajaKXKJ5eL1mWm9iG2ltEKvEDOHc',
474
+ date: this.options.date,
475
+ expirySeconds: this.options.expirySeconds,
476
+ }
477
+
478
+ const messageApi = buildMojaloopPostFxTransfer(params)
479
+ const messageKafka = buildMessagePrepareFx(
480
+ this.options.harness, messageApi
481
+ )
482
+
483
+ return messageKafka
484
+ }
485
+
486
+ public async fulfil(): Promise<this> {
487
+ const mark = this.options.harness.redpandaMark()
488
+ await this.options.transferHandler.fulfil(null, this.buildMessageFulfil())
489
+ await this.options.harness.redpandaDrain(mark, 2)
490
+
491
+ return this
492
+ }
493
+
494
+ public buildMessageFulfil() {
495
+ const params: PutFxTransferBuilderParams = {
496
+ initiatingFsp: this.options.initiatingFsp,
497
+ counterPartyFsp: this.options.counterPartyFsp,
498
+ fulfilment: 'uz0FAeutW6o8Mz7OmJh8ALX6mmsZCcIDOqtE01eo4uI',
499
+ date: this.options.date,
500
+ expirySeconds: this.options.expirySeconds,
501
+ completedTimestamp: new Date(),
502
+ conversionState: "RESERVED"
503
+ }
504
+
505
+ const messageApi = buildMojaloopPutFxTransfer(params)
506
+ const messageKafka = buildMessageFulfilFx(
507
+ this.options.harness,
508
+ messageApi,
509
+ this.options.commitRequestId
510
+ )
511
+
512
+ return messageKafka
513
+ }
514
+
515
+ public async prepareAndFulfil(): Promise<this> {
516
+ await this.prepare()
517
+ await this.fulfil()
518
+
519
+ return this;
520
+ }
521
+
522
+ public async abort(): Promise<this> {
523
+ const putTransfer = buildMojaloopAbortFxTransfer({
524
+ initiatingFsp: this.options.initiatingFsp,
525
+ counterPartyFsp: this.options.counterPartyFsp,
526
+ date: this.options.date,
527
+ })
528
+ const messageKafka = buildMessageAbortFx(
529
+ this.options.harness, putTransfer, this.options.commitRequestId,
530
+ )
531
+ const mark = this.options.harness.redpandaMark()
532
+ await this.options.transferHandler.fulfil(null, messageKafka)
533
+ await this.options.harness.redpandaDrain(mark, 2)
534
+
535
+ return this
536
+ }
537
+ }
538
+
539
+ export class ForexBuilder {
540
+ private harness!: Harness
541
+ private transferHandler!: {
542
+ prepare: (error: any, message: any) => Promise<any>
543
+ fulfil: (error: any, message: any) => Promise<any>
544
+ }
545
+
546
+ deps(harness: Harness, transferHandler: {
547
+ prepare: (error: any, message: any) => Promise<any>,
548
+ fulfil: (error: any, message: any) => Promise<any>,
549
+ }): this {
550
+ this.harness = harness
551
+ this.transferHandler = transferHandler
552
+
553
+ return this
554
+ }
555
+
556
+ commitRequestId(commitRequestId: string) {
557
+ assert(commitRequestId)
558
+ this._commitRequestId = commitRequestId
559
+ return this
560
+ }
561
+
562
+ determiningTransferId(determiningTransferId: string) {
563
+ assert(determiningTransferId)
564
+ this._determiningTransferId = determiningTransferId
565
+ return this
566
+ }
567
+
568
+ parties(initiatingFsp: string, counterPartyFsp: string) {
569
+ assert(initiatingFsp)
570
+ assert(counterPartyFsp)
571
+
572
+ this._initiatingFsp = initiatingFsp
573
+ this._counterPartyFsp = counterPartyFsp
574
+
575
+ return this
576
+ }
577
+
578
+ amountType(amountType: 'SEND' | 'RECEIVE') {
579
+ assert(amountType)
580
+ this._amountType = amountType;
581
+ return this
582
+ }
583
+
584
+ amountSource(amount: string, currency: string = 'USD') {
585
+ assert(amount)
586
+ assert(currency)
587
+ this._sourceAmountComplex = {
588
+ amount,
589
+ currency
590
+ }
591
+
592
+ return this
593
+ }
594
+
595
+ amountTarget(amount: string, currency: string = 'USD') {
596
+ assert(amount)
597
+ assert(currency)
598
+ this._targetAmountComplex = {
599
+ amount,
600
+ currency
601
+ }
602
+
603
+ return this
604
+ }
605
+
606
+ date(date: Date) {
607
+ assert(date)
608
+ this._date = date
609
+ return this
610
+ }
611
+
612
+ expiry(seconds: number): this {
613
+ assert(seconds)
614
+ this._expirySeconds = seconds;
615
+
616
+ return this;
617
+ }
618
+
619
+ private _commitRequestId!: string
620
+ private _determiningTransferId!: string
621
+ private _initiatingFsp!: string
622
+ private _counterPartyFsp!: string
623
+ private _amountType: 'SEND' | 'RECEIVE' = 'SEND'
624
+ private _sourceAmountComplex: {
625
+ amount: string,
626
+ currency: string
627
+ } = { amount: '100.00', currency: 'USD' }
628
+ private _targetAmountComplex: {
629
+ amount: string,
630
+ currency: string
631
+ } = { amount: '100.00', currency: 'USD' }
632
+ private _date: Date = new Date();
633
+ private _expirySeconds: number = 30
634
+
635
+ build(): Forex {
636
+ assert(this.harness, 'harness is undefined, did you forget `.deps()`?')
637
+ assert(this.transferHandler, 'transferHandler is undefined, did you forget `.deps()`?')
638
+ assert(this._initiatingFsp)
639
+ assert(this._counterPartyFsp)
640
+
641
+ const options: ForexOptions = {
642
+ harness: this.harness,
643
+ transferHandler: this.transferHandler,
644
+ commitRequestId: this._commitRequestId,
645
+ determiningTransferId: this._determiningTransferId,
646
+ initiatingFsp: this._initiatingFsp,
647
+ counterPartyFsp: this._counterPartyFsp,
648
+ amountType: this._amountType,
649
+ sourceAmountComplex: this._sourceAmountComplex,
650
+ targetAmountComplex: this._targetAmountComplex,
651
+ date: this._date,
652
+ expirySeconds: this._expirySeconds,
653
+ }
654
+
655
+ return new Forex(options)
656
+ }
657
+ }
658
+
659
+ export interface PostTransferBuilderParams {
660
+ payerFsp: string,
661
+ payeeFsp: string,
662
+ transferId: string,
663
+ amountComplex: {
664
+ amount: string,
665
+ currency: string,
666
+ }
667
+ date: Date,
668
+ expirySeconds: number
669
+ }
670
+
671
+ export interface PostFxTransferBuilderParams {
672
+ /**
673
+ * An end-to-end identifier for the confirmation request.
674
+ */
675
+ commitRequestId: string,
676
+
677
+ /**
678
+ * The transaction ID of the transfer to which this currency
679
+ * conversion relates, if the conversion is part of a transfer. If
680
+ * the conversion is a bulk currency purchase, this field should be
681
+ * omitted.
682
+ */
683
+ determiningTransferId: string,
684
+
685
+ /**
686
+ * Identifier for the FSP who is requesting a currency conversion.
687
+ */
688
+ initiatingFsp: string,
689
+
690
+ /**
691
+ * Identifier for the FXP who is performing the currency conversion.
692
+ */
693
+ counterPartyFsp: string,
694
+
695
+ /**
696
+ * Which leg to do the currency conversion on.
697
+ */
698
+ amountType: 'SEND' | 'RECEIVE',
699
+
700
+ sourceAmount: {
701
+ amount: string,
702
+ currency: string,
703
+ },
704
+ targetAmount: {
705
+ amount: string,
706
+ currency: string,
707
+ }
708
+ condition: string
709
+ date: Date
710
+ expirySeconds: number
711
+ }
712
+
713
+ export interface MockQuoteILPResponse {
714
+ quoteId: string,
715
+ transactionId: string,
716
+ transactionType: string,
717
+ payerFsp: string,
718
+ payeeFsp: string,
719
+ transferId: string,
720
+ amountComplex: {
721
+ amount: string,
722
+ currency: string,
723
+ }
724
+ expiration: string,
725
+ }
726
+
727
+ export interface TransferRequest {
728
+ headers: Record<string, string>,
729
+ payload: any,
730
+ }
731
+
732
+ export interface PutTransferBuilderParams {
733
+ payerFsp: string,
734
+ payeeFsp: string,
735
+ transferId: string,
736
+ date: Date,
737
+ transferState: 'COMMITTED' | 'RESERVED',
738
+ amountComplex: {
739
+ amount: string,
740
+ currency: string,
741
+ }
742
+ expirySeconds: number
743
+ }
744
+
745
+ export interface PutFxTransferBuilderParams {
746
+ /**
747
+ * Identifier for the FSP who is requesting a currency conversion.
748
+ */
749
+ initiatingFsp: string,
750
+
751
+ /**
752
+ * Identifier for the FXP who is performing the currency conversion.
753
+ */
754
+ counterPartyFsp: string,
755
+
756
+ date: Date,
757
+ expirySeconds: number
758
+
759
+
760
+ /**
761
+ * The fulfilment of the condition specified for the currency
762
+ * conversion. Mandatory if the conversion has been executed
763
+ * successfully.
764
+ */
765
+ fulfilment: string,
766
+
767
+ /**
768
+ * Time and date when the conversion was executed.
769
+ */
770
+ completedTimestamp: Date,
771
+
772
+ /**
773
+ * The current status of the conversion request.
774
+ */
775
+ conversionState: 'RECEIVED' | 'RESERVED' | 'COMMITTED' | 'ABORTED',
776
+ }
777
+
778
+ export interface AbortTransferBuilderParams {
779
+ payerFsp: string,
780
+ payeeFsp: string,
781
+ date: Date,
782
+ }
783
+
784
+ export interface AbortFxTransferBuilderParams {
785
+ /**
786
+ * Identifier for the FSP who is requesting a currency conversion.
787
+ */
788
+ initiatingFsp: string,
789
+
790
+ /**
791
+ * Identifier for the FXP who is performing the currency conversion.
792
+ */
793
+ counterPartyFsp: string,
794
+
795
+ date: Date,
796
+ }
797
+
798
+ export type QuoteIlpResponse = {
799
+ fulfilment: string;
800
+ ilpPacket: string;
801
+ condition: string;
802
+ }
803
+
804
+ function generateQuoteILPResponse(params: MockQuoteILPResponse): QuoteIlpResponse {
805
+ // Build an imaginary Quote Request/Response to generate the ILP packet, fulfilment and condition.
806
+ const quoteRequest = {
807
+ quoteId: params.quoteId,
808
+ transactionId: params.transactionId,
809
+ transactionType: params.transactionType,
810
+ payee: {
811
+ partyIdInfo: {
812
+ partyIdType: 'MSISDN',
813
+ partyIdentifier: '12346',
814
+ fspId: params.payeeFsp,
815
+ },
816
+ },
817
+ payer: {
818
+ partyIdInfo: {
819
+ partyIdType: 'MSISDN',
820
+ partyIdentifier: '78901',
821
+ fspId: params.payerFsp,
822
+ },
823
+ },
824
+ expiration: params.expiration
825
+ }
826
+ const quoteResponse = {
827
+ transferAmount: {
828
+ amount: params.amountComplex.amount,
829
+ currency: params.amountComplex.currency
830
+ },
831
+ expiration: params.expiration,
832
+ }
833
+
834
+ const {
835
+ fulfilment,
836
+ ilpPacket,
837
+ condition,
838
+ } = ilpService.getQuoteResponseIlp(quoteRequest, quoteResponse)
839
+
840
+ return {
841
+ fulfilment, ilpPacket, condition
842
+ }
843
+ }
844
+
845
+ /**
846
+ * Helpers to build FSPIOP API messages.
847
+ */
848
+
849
+ export function buildMojaloopPostTransfer(params: PostTransferBuilderParams): TransferRequest {
850
+ const dateStr = params.date.toUTCString()
851
+ const expiration = (new Date(params.date.getTime() + 5 * 1000 * params.expirySeconds)).toISOString()
852
+
853
+ const headers = {
854
+ Accept: 'application/vnd.interoperability.transfers+json;version=1.1',
855
+ 'Content-Type': 'application/vnd.interoperability.transfers+json;version=1.1',
856
+ Date: dateStr,
857
+ 'fspiop-source': params.payerFsp,
858
+ 'fspiop-destination': params.payeeFsp,
859
+ // not sure if we need this one
860
+ Host: 'ml-api-adapter.local',
861
+ }
862
+
863
+ const mockQuoteResponse: MockQuoteILPResponse = {
864
+ quoteId: '00001',
865
+ transactionId: '00001',
866
+ transactionType: 'unknown',
867
+ payerFsp: params.payerFsp,
868
+ payeeFsp: params.payeeFsp,
869
+ transferId: params.transferId,
870
+ amountComplex: {
871
+ amount: params.amountComplex.amount,
872
+ currency: params.amountComplex.currency,
873
+ },
874
+ expiration,
875
+ }
876
+ const { condition, ilpPacket } = generateQuoteILPResponse(mockQuoteResponse)
877
+
878
+ const payload = {
879
+ transferId: params.transferId,
880
+ payerFsp: params.payerFsp,
881
+ payeeFsp: params.payeeFsp,
882
+ amount: {
883
+ amount: params.amountComplex.amount,
884
+ currency: params.amountComplex.currency,
885
+ },
886
+ ilpPacket,
887
+ condition,
888
+ expiration,
889
+ }
890
+
891
+ return {
892
+ headers,
893
+ payload
894
+ }
895
+ }
896
+
897
+ export function buildMojaloopPostFxTransfer(params: PostFxTransferBuilderParams): TransferRequest {
898
+ const dateStr = params.date.toUTCString()
899
+ const expiration = (new Date(params.date.getTime() + 5 * 1000 * params.expirySeconds)).toISOString()
900
+
901
+ const headers = {
902
+ Accept: 'application/vnd.interoperability.transfers+json;version=1.1',
903
+ 'Content-Type': 'application/vnd.interoperability.transfers+json;version=1.1',
904
+ Date: dateStr,
905
+ 'fspiop-source': params.initiatingFsp,
906
+ 'fspiop-destination': params.counterPartyFsp,
907
+ Host: 'ml-api-adapter.local',
908
+ }
909
+
910
+ const payload = {
911
+ commitRequestId: params.commitRequestId,
912
+ determiningTransferId: params.determiningTransferId,
913
+ initiatingFsp: params.initiatingFsp,
914
+ counterPartyFsp: params.counterPartyFsp,
915
+ amountType: params.amountType,
916
+ sourceAmount: {
917
+ amount: params.sourceAmount.amount,
918
+ currency: params.sourceAmount.currency,
919
+ },
920
+ targetAmount: {
921
+ amount: params.targetAmount.amount,
922
+ currency: params.targetAmount.currency,
923
+ },
924
+ condition: params.condition,
925
+ expiration
926
+ }
927
+
928
+ return {
929
+ headers,
930
+ payload
931
+ }
932
+ }
933
+
934
+ export function buildMojaloopPutTransfer(params: PutTransferBuilderParams): TransferRequest {
935
+ const expiration = (new Date(params.date.getTime() + 5 * 1000 * params.expirySeconds)).toISOString()
936
+
937
+ const headers = {
938
+ accept: 'application/vnd.interoperability.transfers+json;version=1.1',
939
+ 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1',
940
+ date: params.date.toUTCString(),
941
+ 'fspiop-destination': params.payerFsp,
942
+ 'fspiop-source': params.payeeFsp,
943
+ }
944
+
945
+ const mockQuoteResponse: MockQuoteILPResponse = {
946
+ quoteId: '00001',
947
+ transactionId: '00001',
948
+ transactionType: 'unknown',
949
+ payerFsp: params.payerFsp,
950
+ payeeFsp: params.payeeFsp,
951
+ transferId: params.transferId,
952
+ amountComplex: {
953
+ amount: params.amountComplex.amount,
954
+ currency: params.amountComplex.currency,
955
+ },
956
+ expiration,
957
+ }
958
+ const { fulfilment } = generateQuoteILPResponse(mockQuoteResponse)
959
+
960
+ const payload = {
961
+ transferState: params.transferState,
962
+ fulfilment,
963
+ completedTimestamp: params.date.toISOString(),
964
+ }
965
+
966
+ return {
967
+ headers,
968
+ payload,
969
+ }
970
+ }
971
+
972
+ export function buildMojaloopPutFxTransfer(params: PutFxTransferBuilderParams): TransferRequest {
973
+ const expiration = (new Date(params.date.getTime() + 5 * 1000 * params.expirySeconds)).toISOString()
974
+
975
+ const headers = {
976
+ accept: 'application/vnd.interoperability.transfers+json;version=1.1',
977
+ 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1',
978
+ date: params.date.toUTCString(),
979
+ 'fspiop-destination': params.initiatingFsp,
980
+ 'fspiop-source': params.counterPartyFsp,
981
+ }
982
+
983
+ const payload = {
984
+ conversionState: params.conversionState,
985
+ fulfilment: params.fulfilment,
986
+ completedTimestamp: params.date.toISOString(),
987
+ }
988
+
989
+ return {
990
+ headers,
991
+ payload,
992
+ }
993
+ }
994
+
995
+ export function buildMojaloopAbortTransfer(params: AbortTransferBuilderParams): TransferRequest {
996
+ const headers = {
997
+ accept: 'application/vnd.interoperability.transfers+json;version=1.1',
998
+ 'content-type': 'application/vnd.interoperability.transfers+json;version=1.1',
999
+ date: params.date.toUTCString(),
1000
+ 'fspiop-destination': params.payerFsp,
1001
+ 'fspiop-source': params.payeeFsp,
1002
+ }
1003
+ const payload = {
1004
+ // Ref: https://docs.mojaloop.io/api/fspiop/v1.1/api-definition.html#payee-errors-5-xxx
1005
+ errorInformation: {
1006
+ errorCode: '5100',
1007
+ errorDescription: 'Payer rejected the transfer'
1008
+ }
1009
+ }
1010
+
1011
+ return {
1012
+ headers,
1013
+ payload,
1014
+ }
1015
+ }
1016
+
1017
+ export function buildMojaloopAbortFxTransfer(params: AbortFxTransferBuilderParams): TransferRequest {
1018
+ const headers = {
1019
+ accept: 'application/vnd.interoperability.fxTransfers+json;version=2.0',
1020
+ 'content-type': 'application/vnd.interoperability.fxTransfers+json;version=2.0',
1021
+ date: params.date.toUTCString(),
1022
+ 'fspiop-destination': params.initiatingFsp,
1023
+ 'fspiop-source': params.counterPartyFsp,
1024
+ }
1025
+
1026
+ const payload = {
1027
+ errorInformation: {
1028
+ errorCode: '5100',
1029
+ errorDescription: 'FXP rejected the conversion'
1030
+ }
1031
+ }
1032
+
1033
+ return {
1034
+ headers,
1035
+ payload,
1036
+ }
1037
+ }
1038
+
1039
+ /**
1040
+ * Helpers to format the messages as if they were coming from ml-api-adapter.
1041
+ */
1042
+ export const buildMessagePrepare = (harness: Harness, transfer: TransferRequest) => {
1043
+ const { headers, payload } = transfer
1044
+ return {
1045
+ topic: harness.topicTransferPrepare.topicName,
1046
+ value: {
1047
+ metadata: {
1048
+ event: {
1049
+ action: Enum.Events.Event.Action.PREPARE,
1050
+ }
1051
+ },
1052
+ content: {
1053
+ headers,
1054
+ payload,
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+
1060
+ export const buildMessagePrepareFx = (harness: Harness, transfer: TransferRequest) => {
1061
+ const { headers, payload } = transfer
1062
+ return {
1063
+ topic: harness.topicTransferPrepare.topicName,
1064
+ value: {
1065
+ metadata: {
1066
+ event: {
1067
+ action: Enum.Events.Event.Action.FX_PREPARE,
1068
+ }
1069
+ },
1070
+ content: {
1071
+ headers,
1072
+ payload,
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+
1078
+ export const buildMessageFulfil = (
1079
+ harness: Harness, transfer: TransferRequest, transferId: string
1080
+ ) => {
1081
+ const { headers, payload } = transfer
1082
+ return {
1083
+ topic: harness.topicTransferFulfil.topicName,
1084
+ value: {
1085
+ metadata: {
1086
+ event: {
1087
+ type: Enum.Events.Event.Action.FULFIL,
1088
+ action: Enum.Events.Event.Action.COMMIT,
1089
+ }
1090
+ },
1091
+ content: {
1092
+ uriParams: {
1093
+ id: transferId
1094
+ },
1095
+ headers,
1096
+ payload,
1097
+ }
1098
+ }
1099
+ }
1100
+ }
1101
+
1102
+ export const buildMessageFulfilFx = (
1103
+ harness: Harness, transfer: TransferRequest, transferId: string
1104
+ ) => {
1105
+ const { headers, payload } = transfer
1106
+ return {
1107
+ topic: harness.topicTransferFulfil.topicName,
1108
+ value: {
1109
+ metadata: {
1110
+ event: {
1111
+ type: Enum.Events.Event.Type.FULFIL,
1112
+ action: Enum.Events.Event.Action.FX_RESERVE,
1113
+ }
1114
+ },
1115
+ content: {
1116
+ uriParams: {
1117
+ id: transferId
1118
+ },
1119
+ headers,
1120
+ payload,
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ export const buildMessageAbort = (
1127
+ harness: Harness, transfer: TransferRequest, transferId: string
1128
+ ) => {
1129
+ const { headers, payload } = transfer
1130
+ return {
1131
+ topic: harness.topicTransferFulfil.topicName,
1132
+ value: {
1133
+ metadata: {
1134
+ event: {
1135
+ type: Enum.Events.Event.Action.FULFIL,
1136
+ action: Enum.Events.Event.Action.ABORT,
1137
+ }
1138
+ },
1139
+ content: {
1140
+ uriParams: {
1141
+ id: transferId
1142
+ },
1143
+ headers,
1144
+ payload,
1145
+ }
1146
+ }
1147
+ }
1148
+ }
1149
+
1150
+ export const buildMessageAbortFx = (
1151
+ harness: Harness, transfer: TransferRequest, commitRequestId: string
1152
+ ) => {
1153
+ const { headers, payload } = transfer
1154
+ return {
1155
+ topic: harness.topicTransferFulfil.topicName,
1156
+ value: {
1157
+ metadata: {
1158
+ event: {
1159
+ type: Enum.Events.Event.Type.FULFIL,
1160
+ action: Enum.Events.Event.Action.FX_ABORT,
1161
+ }
1162
+ },
1163
+ content: {
1164
+ uriParams: {
1165
+ id: commitRequestId
1166
+ },
1167
+ headers,
1168
+ payload,
1169
+ }
1170
+ }
1171
+ }
1172
+ }
1173
+
1174
+ export const buildMessageForwarded = (
1175
+ harness: Harness,
1176
+ transfer: { transferId: string, proxyId: string }
1177
+ ) => {
1178
+ const { transferId, proxyId } = transfer
1179
+
1180
+ return {
1181
+ topic: harness.topicTransferPrepare.topicName,
1182
+ value: {
1183
+ metadata: {
1184
+ event: {
1185
+ type: Enum.Events.Event.Type.PREPARE,
1186
+ action: Enum.Events.Event.Action.FORWARDED
1187
+ }
1188
+ },
1189
+ content: {
1190
+ payload: {
1191
+ proxyId,
1192
+ transferId
1193
+ }
1194
+ }
1195
+ }
1196
+ }
1197
+ }
1198
+
1199
+ export const buildMessageForwardedFx = (
1200
+ harness: Harness,
1201
+ fxTransfer: { commitRequestId: string, proxyId: string }
1202
+ ) => {
1203
+ const { commitRequestId, proxyId } = fxTransfer
1204
+
1205
+ return {
1206
+ topic: harness.topicTransferPrepare.topicName,
1207
+ value: {
1208
+ metadata: {
1209
+ event: {
1210
+ type: Enum.Events.Event.Type.PREPARE,
1211
+ action: Enum.Events.Event.Action.FX_FORWARDED
1212
+ }
1213
+ },
1214
+ content: {
1215
+ payload: {
1216
+ proxyId,
1217
+ commitRequestId
1218
+ }
1219
+ }
1220
+ }
1221
+ }
1222
+ }