@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.
- package/.grype.yaml +3 -0
- package/.license-scanner.json +16 -0
- package/.ncurc.yaml +19 -13
- package/.nvmrc +1 -1
- package/.nycrc.yml +14 -2
- package/CHANGELOG.md +51 -0
- package/Dockerfile +13 -10
- package/README.md +39 -89
- package/audit-ci.jsonc +1 -1
- package/config/default.json +60 -3
- package/config/deprecated.default-settlement.json +160 -0
- package/docker/ml-api-adapter/default.json +28 -0
- package/docker-compose.yml +6 -0
- package/nodemon.json +6 -0
- package/package.json +128 -64
- package/{sbom-v19.13.2.csv → sbom-v20.0.0.csv} +886 -736
- package/scripts/_wait4_all.js +5 -5
- package/scripts/transferSettlementTemp/interchangeFeeCalculation.js +51 -0
- package/src/api/index.ts +30 -0
- package/src/api/interface/swagger.json +2249 -2054
- package/src/api/participants/routes.js +1 -1
- package/src/api/root/handler.int.ts +55 -0
- package/src/api/routes.js +7 -6
- package/src/api/server.ts +27 -0
- package/src/api/server.unit.ts +16 -0
- package/src/api/settlementModels/routes.js +4 -4
- package/src/domain/participant/index.d.ts +47 -0
- package/src/domain/participant/index.int.ts +724 -0
- package/src/domain/participant/index.js +4 -5
- package/src/domain/position/fx-prepare.js +1 -1
- package/src/domain/position/fx-timeout-reserved.js +6 -0
- package/src/domain/position/prepare.js +1 -1
- package/src/handlers/admin/handler.js +1 -1
- package/src/handlers/index.js +30 -0
- package/src/handlers/positions/handler.js +0 -2
- package/src/handlers/positions/handlerBatch.js +5 -5
- package/src/handlers/register.js +44 -29
- package/src/handlers/timeouts/handler.js +1 -4
- package/src/handlers/transfers/fx-abort.int.ts +209 -0
- package/src/handlers/transfers/fx-handlers.int.ts +275 -0
- package/src/handlers/transfers/fx-timeout.int.ts +284 -0
- package/src/handlers/transfers/handler.js +1 -3
- package/src/handlers/transfers/handlers.int.ts +538 -0
- package/src/handlers/transfers/prepare.int.ts +1283 -0
- package/src/handlers/transfers/prepare.js +2 -2
- package/src/knexfile.js +19 -0
- package/src/lib/cache.js +1 -1
- package/src/lib/config/index.ts +60 -0
- package/src/lib/config/resolver.ts +231 -0
- package/src/lib/config/resolver.unit.ts +72 -0
- package/src/lib/config/types.ts +372 -0
- package/src/lib/config/util.ts +523 -0
- package/src/lib/config/util.unit.ts +825 -0
- package/src/lib/migrator.js +3 -3
- package/src/lib/proxyCache.js +3 -3
- package/{migrations → src/migrations}/100100_event.js +1 -1
- package/{migrations → src/migrations}/110100_contactType.js +1 -1
- package/{migrations → src/migrations}/110200_currency.js +1 -1
- package/{migrations → src/migrations}/110201_currency-scale.js +1 -1
- package/{migrations → src/migrations}/110300_endpointType.js +1 -1
- package/{migrations → src/migrations}/110400_ledgerEntryType.js +1 -1
- package/{migrations → src/migrations}/110450_ledgerAccountType.js +1 -1
- package/{migrations → src/migrations}/110500_participantLimitType.js +1 -1
- package/{migrations → src/migrations}/110600_transferParticipantRoleType.js +1 -1
- package/{migrations → src/migrations}/110700_transferState.js +1 -1
- package/{migrations → src/migrations}/110900_settlementState.js +1 -1
- package/{migrations → src/migrations}/112000_bulkTransferState.js +1 -1
- package/{migrations → src/migrations}/112100_bulkProcessingState.js +1 -1
- package/{migrations → src/migrations}/200100_participant.js +1 -1
- package/{migrations → src/migrations}/200200_participantContact.js +1 -1
- package/{migrations → src/migrations}/200300_participantEndpoint.js +1 -1
- package/{migrations → src/migrations}/200400_participantParty.js +1 -1
- package/{migrations → src/migrations}/200600_token.js +1 -1
- package/{migrations → src/migrations}/200601_token-indexes.js +1 -1
- package/{migrations → src/migrations}/300100_transferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/300150_bulkTransferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/300200_transfer.js +2 -2
- package/{migrations → src/migrations}/300202_transfer-decimal184.js +1 -1
- package/{migrations → src/migrations}/300250_bulkTransfer.js +2 -3
- package/{migrations → src/migrations}/300275_bulkTransferAssociation.js +2 -3
- package/{migrations → src/migrations}/300300_ilpPacket.js +1 -1
- package/{migrations → src/migrations}/300400_transferStateChange.js +1 -1
- package/{migrations → src/migrations}/300450_bulkTransferStateChange.js +1 -1
- package/{migrations → src/migrations}/300500_segment.js +1 -1
- package/{migrations → src/migrations}/310100_participantCurrency.js +1 -1
- package/{migrations → src/migrations}/310200_transferParticipant.js +1 -1
- package/{migrations → src/migrations}/310202_transferParticipant-decimal184.js +1 -1
- package/{migrations → src/migrations}/310204_transferParticipant-participantId.js +2 -2
- package/{migrations → src/migrations}/310300_participantPosition.js +1 -1
- package/{migrations → src/migrations}/310302_participantPosition-decimal184.js +1 -1
- package/{migrations → src/migrations}/310400_participantPositionChange.js +1 -1
- package/{migrations → src/migrations}/310402_participantPositionChange-decimal184.js +1 -1
- package/{migrations → src/migrations}/310403_participantPositionChange-participantCurrencyId.js +2 -2
- package/{migrations → src/migrations}/310404_participantPositionChange-change.js +2 -2
- package/{migrations → src/migrations}/310500_participantLimit.js +1 -1
- package/{migrations → src/migrations}/310502_participantLimit-decimal184.js +1 -1
- package/{migrations → src/migrations}/400100_settlementWindow.js +1 -1
- package/{migrations → src/migrations}/400200_settlement.js +1 -1
- package/{migrations → src/migrations}/400300_settlementSettlementWindow.js +1 -1
- package/{migrations → src/migrations}/400301_settlementSettlementWindow-indexes.js +0 -1
- package/{migrations → src/migrations}/400400_settlementWindowStateChange.js +1 -1
- package/{migrations → src/migrations}/400500_settlementTransferParticipant.js +1 -1
- package/{migrations → src/migrations}/400502_settlementTransferParticipant-decimal184.js +1 -1
- package/{migrations → src/migrations}/400600_settlementParticipantCurrency.js +1 -1
- package/{migrations → src/migrations}/400602_settlementParticipantCurrency-decimal184.js +1 -1
- package/{migrations → src/migrations}/400700_settlementParticipantCurrencyStateChange.js +1 -1
- package/{migrations → src/migrations}/400800_settlementStateChange.js +1 -1
- package/{migrations → src/migrations}/400900_settlementWindowContent.js +1 -1
- package/{migrations → src/migrations}/401000_settlementWindowContentStateChange.js +1 -1
- package/{migrations → src/migrations}/401001_settlementWindowContentStateChange-indexes.js +6 -6
- package/{migrations → src/migrations}/401200_settlementContentAggregation.js +1 -1
- package/{migrations → src/migrations}/401300_settlementModel.js +1 -1
- package/{migrations → src/migrations}/410100_transferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410102_transferFulfilmentDuplicateCheck-dropForeignTransferId.js +1 -1
- package/{migrations → src/migrations}/410103_transferFulfilmentDuplicateCheck-hashNullable.js +1 -1
- package/{migrations → src/migrations}/410200_transferFulfilment.js +1 -1
- package/{migrations → src/migrations}/410202_transferFulfilment-ilpFulfilment.js +1 -1
- package/{migrations → src/migrations}/410203_transferFulfilment-foreignTransferFulfilmentId.js +1 -1
- package/{migrations → src/migrations}/410240_bulkTransferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410250_bulkTransferFulfilment.js +1 -1
- package/{migrations → src/migrations}/410300_transferExtension.js +1 -1
- package/{migrations → src/migrations}/410302_transferExtension-transferErrorId.js +1 -1
- package/{migrations → src/migrations}/410350_bulkTransferExtension.js +1 -1
- package/{migrations → src/migrations}/410400_transferTimeout.js +1 -1
- package/{migrations → src/migrations}/410450_transferErrorDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/410500_transferError.js +1 -1
- package/{migrations → src/migrations}/410502_transferError-foreignTransferErrorDuplicateCheckId.js +1 -1
- package/{migrations → src/migrations}/410550_bulkTransferError.js +1 -1
- package/{migrations → src/migrations}/410600_expiringTransfer.js +1 -1
- package/{migrations → src/migrations}/500401_quote-hotfix-2719.js +8 -8
- package/{migrations → src/migrations}/500501_feature-fixSubIdRef.js +12 -12
- package/{migrations → src/migrations}/500601_party-2480.js +3 -3
- package/{migrations → src/migrations}/501001_quoteExtension-quote-service-174.js +1 -2
- package/{migrations → src/migrations}/501002_quoteExtension-2522.js +3 -3
- package/{migrations → src/migrations}/600010_fxTransferType.js +1 -1
- package/{migrations → src/migrations}/600012_fxParticipantCurrencyType.js +1 -1
- package/{migrations → src/migrations}/600100_fxTransferDuplicateCheck.js +2 -2
- package/{migrations → src/migrations}/600110_fxTransferErrorDuplicateCheck.js.js +1 -1
- package/{migrations → src/migrations}/600200_fxTransfer.js +2 -2
- package/{migrations → src/migrations}/600400_fxTransferStateChange.js +1 -1
- package/{migrations → src/migrations}/600501_fxWatchList.js +2 -2
- package/{migrations → src/migrations}/600600_fxTransferFulfilmentDuplicateCheck.js +1 -1
- package/{migrations → src/migrations}/600700_fxTransferFulfilment.js +1 -1
- package/{migrations → src/migrations}/600800_fxTransferExtension.js +1 -1
- package/{migrations → src/migrations}/601400_fxTransferTimeout.js +1 -1
- package/{migrations → src/migrations}/601500_fxTransferError.js +1 -1
- package/{migrations → src/migrations}/610200_fxTransferParticipant.js +1 -1
- package/{migrations → src/migrations}/610202_fxTransferParticipant-participantId.js +2 -2
- package/{migrations → src/migrations}/610403_participantPositionChange-fxTransfer.js +1 -1
- package/{migrations → src/migrations}/800100_quotePartyView.js +1 -1
- package/{migrations → src/migrations}/800101_feature-fixSubIdRef.js +1 -1
- package/{migrations → src/migrations}/800200_quoteView.js +1 -1
- package/{migrations → src/migrations}/800300_quoteResponseView.js +1 -1
- package/{migrations → src/migrations}/900100_foreign-keys.js +18 -18
- package/{migrations → src/migrations}/900101_foreign-keys.js +6 -6
- package/{migrations → src/migrations}/910100_feature904DDL.js +7 -7
- package/{migrations → src/migrations}/910101_feature904DataMigration.js +1 -1
- package/{migrations → src/migrations}/910102_feature949DataMigration.js +1 -1
- package/{migrations → src/migrations}/910103_feature1093SettlementByCurrency.js +3 -3
- package/{migrations → src/migrations}/940101_quotePartyIdInfoExtension-2522.js +25 -25
- package/{migrations → src/migrations}/950101_transferParticipantStateChange.js +2 -2
- package/{migrations → src/migrations}/950102_settlementModel-adjustPosition.js +2 -2
- package/{migrations → src/migrations}/950103_dropTransferParticipantStateChange.js +3 -3
- package/{migrations → src/migrations}/950104_settlementModel-settlementAccountTypeId.js +3 -3
- package/src/migrations/950107_settlementWindowContent_foreign_settlementModel.js +63 -0
- package/{migrations → src/migrations}/950108_participantProxy.js +1 -2
- package/{migrations → src/migrations}/950120_fxQuoteConversionTerms_sourceAmount_nullable.js +1 -1
- package/{migrations → src/migrations}/960100_create_externalParticipant.js +2 -2
- package/{migrations → src/migrations}/960110_alter_transferParticipant__addFiled_externalParticipantId.js +2 -2
- package/{migrations → src/migrations}/961001_transferForwarded.js +1 -1
- package/{migrations → src/migrations}/961002_fxTransferForwarded.js +1 -1
- package/src/models/participant/externalParticipant.int.ts +85 -0
- package/src/models/participant/facade.js +4 -2
- package/src/models/participant/participantCurrencyCached.js +1 -1
- package/src/models/position/facade.js +8 -2
- package/src/models/transfer/facade.int.ts +139 -0
- package/src/models/transfer/facade.js +8 -3
- package/src/models/transfer/transferDuplicateCheck.js +2 -2
- package/src/models/transfer/transferError.int.ts +112 -0
- package/src/models/transfer/transferExtension.int.ts +137 -0
- package/src/models/transfer/transferStateChange.int.ts +117 -0
- package/{seeds → src/seeds}/participant.js +1 -1
- package/{seeds → src/seeds}/settlementInterchange.js +1 -0
- package/src/settlement/api/handlerRoutes.js +71 -0
- package/src/settlement/api/handlers/health.js +10 -0
- package/src/settlement/api/handlers/index.js +57 -0
- package/src/settlement/api/handlers/settlementWindows/{id}.js +103 -0
- package/src/settlement/api/handlers/settlementWindows.js +77 -0
- package/src/settlement/api/handlers/settlements/{id}.js +133 -0
- package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}/accounts/{aid}.js +127 -0
- package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}.js +127 -0
- package/src/settlement/api/handlers/settlements.js +114 -0
- package/src/settlement/api/openapiRouting.js +87 -0
- package/src/settlement/api/routes.js +176 -0
- package/src/settlement/domain/rules/index.js +46 -0
- package/src/settlement/domain/settlement/index.js +405 -0
- package/src/settlement/domain/settlementWindow/index.js +117 -0
- package/src/settlement/domain/transactions/index.js +83 -0
- package/src/settlement/domain/transferSettlement/index.js +57 -0
- package/src/settlement/handlers/deferredSettlement/handler.js +180 -0
- package/src/settlement/handlers/grossSettlement/handler.js +180 -0
- package/src/settlement/handlers/index.js +101 -0
- package/src/settlement/handlers/register.js +92 -0
- package/src/settlement/handlers/rules/handler.js +164 -0
- package/src/settlement/interface/swagger-handler.json +34 -0
- package/src/settlement/interface/swagger.json +1560 -0
- package/src/settlement/lib/db.js +32 -0
- package/src/settlement/lib/kafka/index.js +41 -0
- package/src/settlement/lib/kafka/producer.js +142 -0
- package/src/settlement/lib/scriptEngine.js +99 -0
- package/src/settlement/lib/scriptsLoader.js +175 -0
- package/src/settlement/lib/utility.js +243 -0
- package/src/settlement/models/ilpPackets/ilpPacket.js +42 -0
- package/src/settlement/models/lib/enums.js +192 -0
- package/src/settlement/models/misc/migrationLock.js +53 -0
- package/src/settlement/models/settlement/facade.js +1652 -0
- package/src/settlement/models/settlement/index.js +55 -0
- package/src/settlement/models/settlement/participantCurrency.js +48 -0
- package/src/settlement/models/settlement/settlement.js +49 -0
- package/src/settlement/models/settlement/settlementModel.js +44 -0
- package/src/settlement/models/settlement/settlementParticipantCurrency.js +61 -0
- package/src/settlement/models/settlement/settlementStateChange.js +44 -0
- package/src/settlement/models/settlement/settlementTransferParticipant.js +46 -0
- package/src/settlement/models/settlementWindow/facade.js +345 -0
- package/src/settlement/models/settlementWindow/index.js +47 -0
- package/src/settlement/models/settlementWindow/settlementWindowStateChange.js +55 -0
- package/src/settlement/models/settlementWindowContent/facade.js +79 -0
- package/src/settlement/models/settlementWindowContent/index.js +40 -0
- package/src/settlement/models/settlementWindowContent/settlementWindowContentStateChange.js +53 -0
- package/src/settlement/models/transferSettlement/facade.js +363 -0
- package/src/settlement/models/transferSettlement/index.js +42 -0
- package/src/settlement/shared/logger/index.js +7 -0
- package/src/settlement/shared/plugins.js +88 -0
- package/src/settlement/shared/setup.js +224 -0
- package/src/settlement/utils/truthyProperty.js +37 -0
- package/src/shared/legacyUtilShim.js +20 -0
- package/src/shared/plugins.js +3 -3
- package/src/shared/results.ts +22 -0
- package/src/shared/results.unit.ts +38 -0
- package/src/shared/setup.js +14 -1
- package/src/testing/api-helpers.ts +1222 -0
- package/src/testing/exec-async.ts +67 -0
- package/src/testing/harness.snapshot.sql +3895 -0
- package/src/testing/harness.test.ts +15 -0
- package/src/testing/harness.ts +1160 -0
- package/src/testing/kafka.ts +221 -0
- package/src/testing/prng.ts +82 -0
- package/src/testing/rdkafka.test.ts +263 -0
- package/src/testing/run.ts +559 -0
- package/src/testing/snapshot.test.ts +92 -0
- package/src/testing/snapshot.ts +549 -0
- package/src/testing/tap-stream.ts +164 -0
- package/src/testing/types.ts +95 -0
- package/src/testing/util.ts +333 -0
- package/src/types/central-services-error-handling.d.ts +72 -0
- package/src/types/central-services-stream.d.ts +148 -0
- package/test-integration.Dockerfile +0 -2
- package/test.Dockerfile +0 -2
- package/tsconfig.json +40 -0
- package/config/knexfile.js +0 -19
- package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +0 -63
- package/src/api/index.js +0 -16
- package/src/lib/config.js +0 -81
- /package/{migrations → src/migrations}/110101_contactType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110301_endpointType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110401_ledgerEntryType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110451_ledgerAccountType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110501_participantLimitType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110601_transferParticipantRoleType-indexes.js +0 -0
- /package/{migrations → src/migrations}/110800_settlementWindowState.js +0 -0
- /package/{migrations → src/migrations}/111000_amountType.js +0 -0
- /package/{migrations → src/migrations}/111001_amountType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111100_balanceOfPayments.js +0 -0
- /package/{migrations → src/migrations}/111101_balanceOfPayments-indexes.js +0 -0
- /package/{migrations → src/migrations}/111200_partyIdentifierType.js +0 -0
- /package/{migrations → src/migrations}/111201_partyIdentifierType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111300_partyType.js +0 -0
- /package/{migrations → src/migrations}/111301_partyType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111400_quoteDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/111500_transactionInitiator.js +0 -0
- /package/{migrations → src/migrations}/111501_transactionInitiator-indexes.js +0 -0
- /package/{migrations → src/migrations}/111600_transactionInitiatorType.js +0 -0
- /package/{migrations → src/migrations}/111601_transactionInitiatorType-indexes.js +0 -0
- /package/{migrations → src/migrations}/111700_settlementDelay.js +0 -0
- /package/{migrations → src/migrations}/111701_settlementDelay-indexes.js +0 -0
- /package/{migrations → src/migrations}/111800_settlementInterchange.js +0 -0
- /package/{migrations → src/migrations}/111801_settlementInterchange-indexes.js +0 -0
- /package/{migrations → src/migrations}/111900_settlementGranularity.js +0 -0
- /package/{migrations → src/migrations}/111901_settlementGranularity-indexes.js +0 -0
- /package/{migrations → src/migrations}/112101_bulkProcessingState-indexes.js +0 -0
- /package/{migrations → src/migrations}/200101_participant-indexes.js +0 -0
- /package/{migrations → src/migrations}/200201_participantContact-indexes.js +0 -0
- /package/{migrations → src/migrations}/200301_participantEndpoint-indexes.js +0 -0
- /package/{migrations → src/migrations}/200401_participantParty-indexes.js +0 -0
- /package/{migrations → src/migrations}/300201_transfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/300251_bulkTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/300276_bulkTransferAssociation-indexes.js +0 -0
- /package/{migrations → src/migrations}/300401_transferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/300451_bulkTransferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/300501_segment-indexes.js +0 -0
- /package/{migrations → src/migrations}/310101_participantCurrency-indexes.js +0 -0
- /package/{migrations → src/migrations}/310201_transferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/310203_transferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/310301_participantPosition-indexes.js +0 -0
- /package/{migrations → src/migrations}/310401_participantPositionChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/310405_participantPositionChange-positionChange.js +0 -0
- /package/{migrations → src/migrations}/310406_participantPositionChange-positionChange-revert.js +0 -0
- /package/{migrations → src/migrations}/310501_participantLimit-indexes.js +0 -0
- /package/{migrations → src/migrations}/310503_participantLimit-indexes-composite.js +0 -0
- /package/{migrations → src/migrations}/400401_settlmentWindowStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/400501_settlementTransferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/400601_settlementParticipantCurrency-indexes.js +0 -0
- /package/{migrations → src/migrations}/400701_settlementParticipantCurrencyStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/400801_settlementStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/401002_settlementWindowContent-indexes.js +0 -0
- /package/{migrations → src/migrations}/401201_settlementContentAggregation-indexes.js +0 -0
- /package/{migrations → src/migrations}/401301_settlementModel-indexes.js +0 -0
- /package/{migrations → src/migrations}/410101_transferFulfilmentDuplicateCheck-indexes.js +0 -0
- /package/{migrations → src/migrations}/410201_transferFulfilment-indexes.js +0 -0
- /package/{migrations → src/migrations}/410301_transferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/410351_bulkTransferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/410401_transferTimeout-indexes.js +0 -0
- /package/{migrations → src/migrations}/410501_transferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/410551_bulkTransferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/410601_expiringTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/500100_transactionReference.js +0 -0
- /package/{migrations → src/migrations}/500101_transactionReference-indexes.js +0 -0
- /package/{migrations → src/migrations}/500200_transactionScenario.js +0 -0
- /package/{migrations → src/migrations}/500201_transactionScenario-indexes.js +0 -0
- /package/{migrations → src/migrations}/500300_transactionSubScenario.js +0 -0
- /package/{migrations → src/migrations}/500400_quote.js +0 -0
- /package/{migrations → src/migrations}/500500_quoteParty.js +0 -0
- /package/{migrations → src/migrations}/500600_party.js +0 -0
- /package/{migrations → src/migrations}/500700_quoteResponse.js +0 -0
- /package/{migrations → src/migrations}/500800_quoteResponseIlpPacket.js +0 -0
- /package/{migrations → src/migrations}/500900_geoCode.js +0 -0
- /package/{migrations → src/migrations}/501000_quoteExtension.js +0 -0
- /package/{migrations → src/migrations}/501100_quoteResponseDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/501200_quoteError.js +0 -0
- /package/{migrations → src/migrations}/501300_transferRules.js +0 -0
- /package/{migrations → src/migrations}/600011_fxTransferType-indexes.js +0 -0
- /package/{migrations → src/migrations}/600013_fxParticipantCurrencyType-indexes.js +0 -0
- /package/{migrations → src/migrations}/600201_fxTransfer-indexes.js +0 -0
- /package/{migrations → src/migrations}/600401_fxTransferStateChange-indexes.js +0 -0
- /package/{migrations → src/migrations}/600502_fxWatchList-indexes.js +0 -0
- /package/{migrations → src/migrations}/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +0 -0
- /package/{migrations → src/migrations}/600701_fxTransferFulfilment-indexes.js +0 -0
- /package/{migrations → src/migrations}/601401_fxTransferTimeout-indexes.js +0 -0
- /package/{migrations → src/migrations}/601501_fxTransferError-indexes.js +0 -0
- /package/{migrations → src/migrations}/610201_fxTransferParticipant-indexes.js +0 -0
- /package/{migrations → src/migrations}/910102_hotfix954.js +0 -0
- /package/{migrations → src/migrations}/940100_quotePartyIdInfoExtension.js +0 -0
- /package/{migrations → src/migrations}/950100_settlementContentAggregation-decimal184.js +0 -0
- /package/{migrations → src/migrations}/950106_unique_settlement_model_ledger_account_currency.js +0 -0
- /package/{migrations → src/migrations}/950109_fxQuote.js +0 -0
- /package/{migrations → src/migrations}/950110_fxQuoteResponse.js +0 -0
- /package/{migrations → src/migrations}/950111_fxQuoteError.js +0 -0
- /package/{migrations → src/migrations}/950113_fxQuoteDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/950114_fxQuoteResponseDuplicateCheck.js +0 -0
- /package/{migrations → src/migrations}/950115_fxQuoteConversionTerms.js +0 -0
- /package/{migrations → src/migrations}/950116_fxQuoteConversionTermsExtension.js +0 -0
- /package/{migrations → src/migrations}/950117_fxQuoteResponseConversionTerms.js +0 -0
- /package/{migrations → src/migrations}/950118_fxQuoteResponseConversionTermsExtension.js +0 -0
- /package/{migrations → src/migrations}/950119_fxCharge.js +0 -0
- /package/{migrations → src/migrations}/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +0 -0
- /package/{migrations → src/migrations}/960112_fxTransfer-indexes2.js +0 -0
- /package/{migrations → src/migrations}/960113_fxTransferExtension-indexes.js +0 -0
- /package/{migrations → src/migrations}/960114_fxTransferTimeout-indexes2.js +0 -0
- /package/{migrations → src/migrations}/961000_participantEndpoint_unique.js +0 -0
- /package/{migrations → src/migrations}/migration.template +0 -0
- /package/{seeds → src/seeds}/amountType.js +0 -0
- /package/{seeds → src/seeds}/balanceOfPayments.js +0 -0
- /package/{seeds → src/seeds}/bulkProcessingState.js +0 -0
- /package/{seeds → src/seeds}/bulkTransferState.js +0 -0
- /package/{seeds → src/seeds}/currency.js +0 -0
- /package/{seeds → src/seeds}/endpointType.js +0 -0
- /package/{seeds → src/seeds}/fxParticipantCurrencyType.js +0 -0
- /package/{seeds → src/seeds}/fxTransferType.js +0 -0
- /package/{seeds → src/seeds}/ledgerAccountType.js +0 -0
- /package/{seeds → src/seeds}/ledgerEntryType.js +0 -0
- /package/{seeds → src/seeds}/participantLimitType.js +0 -0
- /package/{seeds → src/seeds}/partyIdentifierType.js +0 -0
- /package/{seeds → src/seeds}/partyType.js +0 -0
- /package/{seeds → src/seeds}/settlementDelay.js +0 -0
- /package/{seeds → src/seeds}/settlementGranularity.js +0 -0
- /package/{seeds → src/seeds}/settlementState.js +0 -0
- /package/{seeds → src/seeds}/settlementWindow1State.js +0 -0
- /package/{seeds → src/seeds}/settlementWindow2Open.js +0 -0
- /package/{seeds → src/seeds}/transactionInitiator.js +0 -0
- /package/{seeds → src/seeds}/transactionInitiatorType.js +0 -0
- /package/{seeds → src/seeds}/transactionScenario.js +0 -0
- /package/{seeds → src/seeds}/transferParticipantRoleType.js +0 -0
- /package/{seeds → src/seeds}/transferState.js +0 -0
- /package/{seeds → src/seeds}/z1000_settlementModel-deprecated.js- +0 -0
|
@@ -0,0 +1,1283 @@
|
|
|
1
|
+
/*****
|
|
2
|
+
License
|
|
3
|
+
--------------
|
|
4
|
+
Copyright © 2020-2026 Mojaloop Foundation
|
|
5
|
+
The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
10
|
+
|
|
11
|
+
Contributors
|
|
12
|
+
--------------
|
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
|
15
|
+
should be listed with a '*' in the first column. People who have
|
|
16
|
+
contributed from an organization can be listed under the organization
|
|
17
|
+
that actually holds the copyright for their contributions (see the
|
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
20
|
+
optionally within square brackets <email>.
|
|
21
|
+
|
|
22
|
+
* TigerBeetle
|
|
23
|
+
- Lewis Daly <lewis@tigerbeetle.com>
|
|
24
|
+
--------------
|
|
25
|
+
******/
|
|
26
|
+
|
|
27
|
+
import { after, before, describe, it } from "node:test"
|
|
28
|
+
import Harness from '../../testing/harness'
|
|
29
|
+
import { Snapshot } from "../../testing/snapshot"
|
|
30
|
+
import * as ApiHelpers from '../../testing/api-helpers'
|
|
31
|
+
import assert from "node:assert"
|
|
32
|
+
import { assertPositionDiff, sleepSeconds } from "../../testing/util"
|
|
33
|
+
import TimeoutHandler from '../timeouts/handler'
|
|
34
|
+
|
|
35
|
+
const harness = Harness.getInstance()
|
|
36
|
+
let PrepareHandler: any
|
|
37
|
+
let TransferHandler: any
|
|
38
|
+
let PositionBatchHandler: any
|
|
39
|
+
let ExternalParticipantCached: any
|
|
40
|
+
let TransferFacade: any
|
|
41
|
+
let FxTransferService: any
|
|
42
|
+
let proxyCache: any
|
|
43
|
+
|
|
44
|
+
describe('handlers/prepare', () => {
|
|
45
|
+
before(async () => {
|
|
46
|
+
await harness.up('BATCH')
|
|
47
|
+
await harness.setupGlobals()
|
|
48
|
+
|
|
49
|
+
// Import after bringing up the harness, so that global config is overriden.
|
|
50
|
+
TransferHandler = require('./handler')
|
|
51
|
+
PrepareHandler = require('./prepare')
|
|
52
|
+
PositionBatchHandler = require('../positions/handlerBatch')
|
|
53
|
+
TransferFacade = require('../../models/transfer/facade')
|
|
54
|
+
FxTransferService = require('../../domain/fx/index')
|
|
55
|
+
ExternalParticipantCached = require('../../models/participant/externalParticipantCached')
|
|
56
|
+
proxyCache = require('../../lib/proxyCache')
|
|
57
|
+
await proxyCache.connect()
|
|
58
|
+
await TransferHandler.registerPrepareHandler()
|
|
59
|
+
await TransferHandler.registerFulfilHandler()
|
|
60
|
+
|
|
61
|
+
// Create the hub accounts + settlement model.
|
|
62
|
+
const createHubPayload: ApiHelpers.CreateHubPayload = {
|
|
63
|
+
currencies: ['BWP', 'USD'],
|
|
64
|
+
settlementModels: [
|
|
65
|
+
{
|
|
66
|
+
name: `DEFERRED_MULTILATERAL_NET_BWP`,
|
|
67
|
+
settlementGranularity: "NET",
|
|
68
|
+
settlementInterchange: "MULTILATERAL",
|
|
69
|
+
settlementDelay: "DEFERRED",
|
|
70
|
+
currency: 'BWP',
|
|
71
|
+
requireLiquidityCheck: true,
|
|
72
|
+
ledgerAccountType: "POSITION",
|
|
73
|
+
settlementAccountType: "SETTLEMENT",
|
|
74
|
+
autoPositionReset: true
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: `DEFERRED_MULTILATERAL_NET_USD`,
|
|
78
|
+
settlementGranularity: "NET",
|
|
79
|
+
settlementInterchange: "MULTILATERAL",
|
|
80
|
+
settlementDelay: "DEFERRED",
|
|
81
|
+
currency: 'USD',
|
|
82
|
+
requireLiquidityCheck: true,
|
|
83
|
+
ledgerAccountType: "POSITION",
|
|
84
|
+
settlementAccountType: "SETTLEMENT",
|
|
85
|
+
autoPositionReset: true
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
await ApiHelpers.createHub(harness, createHubPayload)
|
|
90
|
+
// Create 2 test dfsps to transfer between.
|
|
91
|
+
await ApiHelpers.createDfsp(harness, {
|
|
92
|
+
name: 'dfsp_a',
|
|
93
|
+
currencies: ['BWP'],
|
|
94
|
+
isProxy: false,
|
|
95
|
+
initialPostionsAndLimits: [{ initialPosition: 0, value: 100000 }],
|
|
96
|
+
deposits: [10000]
|
|
97
|
+
})
|
|
98
|
+
await ApiHelpers.createDfsp(harness, {
|
|
99
|
+
name: 'dfsp_b',
|
|
100
|
+
currencies: ['BWP', 'USD'],
|
|
101
|
+
isProxy: false,
|
|
102
|
+
initialPostionsAndLimits: [
|
|
103
|
+
{ initialPosition: 0, value: 100000 },
|
|
104
|
+
{ initialPosition: 0, value: 100000 }
|
|
105
|
+
],
|
|
106
|
+
deposits: [10000, 10000]
|
|
107
|
+
})
|
|
108
|
+
await ApiHelpers.createDfsp(harness, {
|
|
109
|
+
name: 'dfsp_a_proxy',
|
|
110
|
+
currencies: ['BWP', 'USD'],
|
|
111
|
+
isProxy: true,
|
|
112
|
+
initialPostionsAndLimits: [
|
|
113
|
+
{ initialPosition: 0, value: 100000 },
|
|
114
|
+
{ initialPosition: 0, value: 100000 }
|
|
115
|
+
],
|
|
116
|
+
deposits: [10000, 10000]
|
|
117
|
+
})
|
|
118
|
+
await ApiHelpers.createDfsp(harness, {
|
|
119
|
+
name: 'dfsp_b_proxy',
|
|
120
|
+
currencies: ['BWP', 'USD'],
|
|
121
|
+
isProxy: true,
|
|
122
|
+
initialPostionsAndLimits: [
|
|
123
|
+
{ initialPosition: 0, value: 100000 },
|
|
124
|
+
{ initialPosition: 0, value: 100000 }
|
|
125
|
+
],
|
|
126
|
+
deposits: [10000, 10000]
|
|
127
|
+
})
|
|
128
|
+
await ApiHelpers.createDfsp(harness, {
|
|
129
|
+
name: 'fxp_a',
|
|
130
|
+
currencies: ['BWP', 'USD'],
|
|
131
|
+
isProxy: false,
|
|
132
|
+
initialPostionsAndLimits: [
|
|
133
|
+
{ initialPosition: 0, value: 100000 },
|
|
134
|
+
{ initialPosition: 0, value: 100000 }
|
|
135
|
+
],
|
|
136
|
+
deposits: [10000, 10000]
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// Set up the proxy1 currencies.
|
|
140
|
+
await proxyCache.getCache().addDfspIdToProxyMapping('external_dfsp_a', 'dfsp_a_proxy')
|
|
141
|
+
await proxyCache.getCache().addDfspIdToProxyMapping('external_dfsp_b', 'dfsp_b_proxy')
|
|
142
|
+
|
|
143
|
+
// Create payment of $100.00 USD from dfsp_a to dfsp_b with id 1000001.
|
|
144
|
+
await ApiHelpers.buildPayment()
|
|
145
|
+
.deps(harness, TransferHandler)
|
|
146
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
147
|
+
.transferId('1000001')
|
|
148
|
+
.amount('1.00', 'BWP')
|
|
149
|
+
.build()
|
|
150
|
+
.prepareAndFulfil()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
after(async () => {
|
|
154
|
+
await proxyCache.disconnect()
|
|
155
|
+
await harness.teardownGlobals()
|
|
156
|
+
await harness.down()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* fxTransfers
|
|
161
|
+
*/
|
|
162
|
+
it('Calculates the obligation between initating and counterparty DFSP.', async () => {
|
|
163
|
+
const payload = {
|
|
164
|
+
commitRequestId: '200001',
|
|
165
|
+
determiningTransferId: '300001',
|
|
166
|
+
initiatingFsp: 'external_dfsp_a',
|
|
167
|
+
counterPartyFsp: 'external_dfsp_b',
|
|
168
|
+
amountType: 'SEND',
|
|
169
|
+
sourceAmount: { currency: 'BWP', amount: '300.33' },
|
|
170
|
+
targetAmount: { currency: 'TZS', amount: '48000' },
|
|
171
|
+
// Mock condition.
|
|
172
|
+
condition: '8x04dj-RKEtfjStajaKXKJ5eL1mWm9iG2ltEKvEDOHc',
|
|
173
|
+
expiration: new Date(Date.now() + (24 * 60 * 60 * 1000))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const obligation = await PrepareHandler.calculateProxyObligation({
|
|
177
|
+
payload,
|
|
178
|
+
isFx: true,
|
|
179
|
+
params: {},
|
|
180
|
+
functionality: 'functionality',
|
|
181
|
+
action: 'action'
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
Snapshot.from(`{
|
|
185
|
+
"isFx": true,
|
|
186
|
+
"payloadClone": {
|
|
187
|
+
"commitRequestId": "200001",
|
|
188
|
+
"determiningTransferId": "300001",
|
|
189
|
+
"initiatingFsp": "dfsp_a_proxy",
|
|
190
|
+
"counterPartyFsp": "dfsp_b_proxy",
|
|
191
|
+
"amountType": "SEND",
|
|
192
|
+
"sourceAmount": {
|
|
193
|
+
"currency": "BWP",
|
|
194
|
+
"amount": "300.33"
|
|
195
|
+
},
|
|
196
|
+
"targetAmount": {
|
|
197
|
+
"currency": "TZS",
|
|
198
|
+
"amount": "48000"
|
|
199
|
+
},
|
|
200
|
+
"condition": "8x04dj-RKEtfjStajaKXKJ5eL1mWm9iG2ltEKvEDOHc",
|
|
201
|
+
"expiration": :ignore
|
|
202
|
+
},
|
|
203
|
+
"isInitiatingFspProxy": true,
|
|
204
|
+
"isCounterPartyFspProxy": true,
|
|
205
|
+
"initiatingFspProxyOrParticipantId": {
|
|
206
|
+
"inScheme": false,
|
|
207
|
+
"proxyId": "dfsp_a_proxy",
|
|
208
|
+
"name": "external_dfsp_a"
|
|
209
|
+
},
|
|
210
|
+
"counterPartyFspProxyOrParticipantId": {
|
|
211
|
+
"inScheme": false,
|
|
212
|
+
"proxyId": "dfsp_b_proxy",
|
|
213
|
+
"name": "external_dfsp_b"
|
|
214
|
+
}
|
|
215
|
+
}`).checkUnwrap(obligation)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('Lazy creates external participants.', async () => {
|
|
219
|
+
let payerExternal = await ExternalParticipantCached.getByName('external_dfsp_a')
|
|
220
|
+
let payeeExternal = await ExternalParticipantCached.getByName('external_dfsp_b')
|
|
221
|
+
assert(payerExternal === undefined)
|
|
222
|
+
assert(payeeExternal === undefined)
|
|
223
|
+
|
|
224
|
+
await ApiHelpers.buildForex()
|
|
225
|
+
.deps(harness, TransferHandler)
|
|
226
|
+
.commitRequestId('2000001')
|
|
227
|
+
.determiningTransferId('3000001')
|
|
228
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
229
|
+
.amountSource('100.00', 'BWP')
|
|
230
|
+
.amountTarget('1.00', 'USD')
|
|
231
|
+
.build()
|
|
232
|
+
.prepareAndFulfil()
|
|
233
|
+
|
|
234
|
+
const payment = ApiHelpers.buildPayment()
|
|
235
|
+
.deps(harness, TransferHandler)
|
|
236
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
237
|
+
.transferId('3000001')
|
|
238
|
+
.amount('1.00', 'USD')
|
|
239
|
+
.fx()
|
|
240
|
+
.build()
|
|
241
|
+
|
|
242
|
+
await payment.prepare()
|
|
243
|
+
|
|
244
|
+
// Assert that the've been created?
|
|
245
|
+
payerExternal = await ExternalParticipantCached.getByName('external_dfsp_a')
|
|
246
|
+
payeeExternal = await ExternalParticipantCached.getByName('external_dfsp_b')
|
|
247
|
+
assert(payerExternal)
|
|
248
|
+
assert(payeeExternal)
|
|
249
|
+
|
|
250
|
+
const [participant] = await TransferFacade.getTransferParticipant('dfsp_a_proxy', '3000001')
|
|
251
|
+
Snapshot.from(`{
|
|
252
|
+
"transferParticipantId": :ignore,
|
|
253
|
+
"transferId": "3000001",
|
|
254
|
+
"participantCurrencyId": :ignore,
|
|
255
|
+
"transferParticipantRoleTypeId": 1,
|
|
256
|
+
"ledgerEntryTypeId": 1,
|
|
257
|
+
"amount": "1.0000",
|
|
258
|
+
"createdDate": :ignore
|
|
259
|
+
"participantId": 4,
|
|
260
|
+
"externalParticipantId": 1
|
|
261
|
+
}`).checkUnwrap(participant)
|
|
262
|
+
|
|
263
|
+
// Fulfil the payment so it doesn't time out later.
|
|
264
|
+
await payment.fulfil()
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('Ignores non COMMITTED/ABORTED fxTransfer on duplicate request.', async () => {
|
|
268
|
+
const forex = ApiHelpers.buildForex()
|
|
269
|
+
.deps(harness, TransferHandler)
|
|
270
|
+
.commitRequestId('2000002')
|
|
271
|
+
.determiningTransferId('3000002')
|
|
272
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
273
|
+
.amountSource('100.00', 'BWP')
|
|
274
|
+
.amountTarget('1.00', 'USD')
|
|
275
|
+
.build()
|
|
276
|
+
|
|
277
|
+
await forex.prepare()
|
|
278
|
+
|
|
279
|
+
// Manually prepare again.
|
|
280
|
+
const mark = harness.redpandaMark()
|
|
281
|
+
await PrepareHandler.prepare(null, forex.buildMessagePrepare())
|
|
282
|
+
await harness.redpandaDrain(mark, 1)
|
|
283
|
+
|
|
284
|
+
const lastTopics = harness.spoolLastTopic(3)
|
|
285
|
+
Snapshot.from(`[
|
|
286
|
+
"topic-transfer-position-batch",
|
|
287
|
+
"topic-notification-event",
|
|
288
|
+
"topic-notification-event"
|
|
289
|
+
]`).checkUnwrap(lastTopics)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('Duplicate fxTransfers callback when in `RECEIVED_FULFIL_DEPENDENT` state.', async () => {
|
|
293
|
+
const forex = ApiHelpers.buildForex()
|
|
294
|
+
.deps(harness, TransferHandler)
|
|
295
|
+
.commitRequestId('2000003')
|
|
296
|
+
.determiningTransferId('3000003')
|
|
297
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
298
|
+
.amountSource('100.00', 'BWP')
|
|
299
|
+
.amountTarget('1.00', 'USD')
|
|
300
|
+
.build()
|
|
301
|
+
|
|
302
|
+
await forex.prepareAndFulfil()
|
|
303
|
+
|
|
304
|
+
// Now send the first message again.
|
|
305
|
+
// await forex.prepare()
|
|
306
|
+
const mark = harness.redpandaMark()
|
|
307
|
+
await PrepareHandler.prepare(null, forex.buildMessagePrepare())
|
|
308
|
+
await harness.redpandaDrain(mark, 1)
|
|
309
|
+
|
|
310
|
+
const lastTopics = harness.spoolLastTopic(1)
|
|
311
|
+
Snapshot.from(`[
|
|
312
|
+
"topic-notification-event"
|
|
313
|
+
]`).checkUnwrap(lastTopics)
|
|
314
|
+
|
|
315
|
+
Snapshot.from(`{
|
|
316
|
+
"completedTimestamp": :ignore,
|
|
317
|
+
"conversionState": "RESERVED",
|
|
318
|
+
"fulfilment": "uz0FAeutW6o8Mz7OmJh8ALX6mmsZCcIDOqtE01eo4uI"
|
|
319
|
+
}`).checkUnwrap(harness.spoolLastPayload(1)[0])
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('Duplicate fxTransfers callback when in `COMMITTED` state.', async () => {
|
|
323
|
+
const forex = ApiHelpers.buildForex()
|
|
324
|
+
.deps(harness, TransferHandler)
|
|
325
|
+
.commitRequestId('2000004')
|
|
326
|
+
.determiningTransferId('3000004')
|
|
327
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
328
|
+
.amountSource('100.00', 'BWP')
|
|
329
|
+
.amountTarget('1.00', 'USD')
|
|
330
|
+
.build()
|
|
331
|
+
|
|
332
|
+
await forex.prepareAndFulfil()
|
|
333
|
+
|
|
334
|
+
// Make the payment.
|
|
335
|
+
await ApiHelpers.buildPayment()
|
|
336
|
+
.deps(harness, TransferHandler)
|
|
337
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
338
|
+
.transferId('3000004')
|
|
339
|
+
.amount('1.00', 'USD')
|
|
340
|
+
.fx()
|
|
341
|
+
.build()
|
|
342
|
+
.prepareAndFulfil()
|
|
343
|
+
|
|
344
|
+
// Now send the first message again.
|
|
345
|
+
const mark = harness.redpandaMark()
|
|
346
|
+
await PrepareHandler.prepare(null, forex.buildMessagePrepare())
|
|
347
|
+
await harness.redpandaDrain(mark, 1)
|
|
348
|
+
|
|
349
|
+
const lastTopics = harness.spoolLastTopic(1)
|
|
350
|
+
Snapshot.from(`[
|
|
351
|
+
"topic-notification-event"
|
|
352
|
+
]`).checkUnwrap(lastTopics)
|
|
353
|
+
|
|
354
|
+
Snapshot.from(`{
|
|
355
|
+
"completedTimestamp": :ignore,
|
|
356
|
+
"conversionState": "COMMITTED",
|
|
357
|
+
"fulfilment": "uz0FAeutW6o8Mz7OmJh8ALX6mmsZCcIDOqtE01eo4uI"
|
|
358
|
+
}`).checkUnwrap(harness.spoolLastPayload(1)[0])
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
it('Duplicate fxTransfers callback when in `ABORTED` state.', async () => {
|
|
362
|
+
const forex = ApiHelpers.buildForex()
|
|
363
|
+
.deps(harness, TransferHandler)
|
|
364
|
+
.commitRequestId('2000005')
|
|
365
|
+
.determiningTransferId('3000005')
|
|
366
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
367
|
+
.amountSource('100.00', 'BWP')
|
|
368
|
+
.amountTarget('1.00', 'USD')
|
|
369
|
+
.build()
|
|
370
|
+
|
|
371
|
+
await forex.prepare()
|
|
372
|
+
await forex.abort()
|
|
373
|
+
|
|
374
|
+
// Now send the first message again.
|
|
375
|
+
const mark = harness.redpandaMark()
|
|
376
|
+
await PrepareHandler.prepare(null, forex.buildMessagePrepare())
|
|
377
|
+
await harness.redpandaDrain(mark, 1)
|
|
378
|
+
|
|
379
|
+
const lastTopics = harness.spoolLastTopic(1)
|
|
380
|
+
Snapshot.from(`[
|
|
381
|
+
"topic-notification-event"
|
|
382
|
+
]`).checkUnwrap(lastTopics)
|
|
383
|
+
|
|
384
|
+
Snapshot.from(`{
|
|
385
|
+
"completedTimestamp": :ignore,
|
|
386
|
+
"conversionState": "ABORTED"
|
|
387
|
+
}`).checkUnwrap(harness.spoolLastPayload(1)[0])
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Proxy
|
|
392
|
+
*/
|
|
393
|
+
it('Updates the transfer state on prepare forwarded action.', async () => {
|
|
394
|
+
const transferId = '3000006'
|
|
395
|
+
const payment = ApiHelpers.buildPayment()
|
|
396
|
+
.deps(harness, TransferHandler)
|
|
397
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
398
|
+
.transferId(transferId)
|
|
399
|
+
.amount('45.67', 'BWP')
|
|
400
|
+
.build()
|
|
401
|
+
|
|
402
|
+
await payment.prepare()
|
|
403
|
+
|
|
404
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
405
|
+
transferId,
|
|
406
|
+
proxyId: 'dfsp_a_proxy'
|
|
407
|
+
})
|
|
408
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
409
|
+
|
|
410
|
+
const transfer = await TransferFacade.getById(transferId)
|
|
411
|
+
assert.equal(transfer.transferState, 'RESERVED_FORWARDED')
|
|
412
|
+
|
|
413
|
+
// Fulfil the payment so it doesn't time out later.
|
|
414
|
+
await payment.fulfil()
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('Should not time out the prepared payment in RESERVED_FORWARDED state.', async () => {
|
|
418
|
+
const transferId = '3000007'
|
|
419
|
+
const payment = ApiHelpers.buildPayment()
|
|
420
|
+
.deps(harness, TransferHandler)
|
|
421
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
422
|
+
.transferId(transferId)
|
|
423
|
+
.amount('100.00', 'BWP')
|
|
424
|
+
.expiry(1)
|
|
425
|
+
.build()
|
|
426
|
+
|
|
427
|
+
const [positionPayerA] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
428
|
+
await payment.prepare()
|
|
429
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
430
|
+
transferId: transferId,
|
|
431
|
+
proxyId: 'dfsp_a_proxy'
|
|
432
|
+
})
|
|
433
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
434
|
+
|
|
435
|
+
const [positionPayerB] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
436
|
+
assertPositionDiff('payer', positionPayerA, positionPayerB, {
|
|
437
|
+
pending: 0,
|
|
438
|
+
posted: 100
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const transfer = await TransferFacade.getById(transferId)
|
|
442
|
+
assert.equal(transfer.transferState, 'RESERVED_FORWARDED')
|
|
443
|
+
|
|
444
|
+
await sleepSeconds(10)
|
|
445
|
+
// We don't run the timeout handler in these tests, but instead just call timeout()
|
|
446
|
+
// directly. This makes the tests more predictable and deterministic.
|
|
447
|
+
const mark = harness.redpandaMark()
|
|
448
|
+
await TimeoutHandler.timeout()
|
|
449
|
+
await harness.redpandaDrain(mark, 1)
|
|
450
|
+
const [positionPayerC] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
451
|
+
assertPositionDiff('payer', positionPayerB, positionPayerC, {
|
|
452
|
+
pending: 0,
|
|
453
|
+
posted: 0
|
|
454
|
+
})
|
|
455
|
+
await payment.abort()
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
it('Completes a forwarded payment.', async () => {
|
|
459
|
+
const transferId = '3000008'
|
|
460
|
+
const payment = ApiHelpers.buildPayment()
|
|
461
|
+
.deps(harness, TransferHandler)
|
|
462
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
463
|
+
.transferId(transferId)
|
|
464
|
+
.amount('100.00', 'BWP')
|
|
465
|
+
.expiry(120)
|
|
466
|
+
.build()
|
|
467
|
+
|
|
468
|
+
const [positionPayerA] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
469
|
+
await payment.prepare()
|
|
470
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
471
|
+
transferId: transferId,
|
|
472
|
+
proxyId: 'dfsp_a_proxy'
|
|
473
|
+
})
|
|
474
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
475
|
+
|
|
476
|
+
const [positionPayerB, positionPayeeB] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
477
|
+
assertPositionDiff('payer', positionPayerA, positionPayerB, {
|
|
478
|
+
pending: 0,
|
|
479
|
+
posted: 100
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
let transfer = await TransferFacade.getById(transferId)
|
|
483
|
+
assert.equal(transfer.transferState, 'RESERVED_FORWARDED')
|
|
484
|
+
|
|
485
|
+
await payment.fulfil()
|
|
486
|
+
transfer = await TransferFacade.getById(transferId)
|
|
487
|
+
assert.equal(transfer.transferState, 'COMMITTED')
|
|
488
|
+
|
|
489
|
+
const [positionPayerC, positionPayeeC] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
490
|
+
assertPositionDiff('payer', positionPayerB, positionPayerC, {
|
|
491
|
+
pending: 0,
|
|
492
|
+
posted: 0
|
|
493
|
+
})
|
|
494
|
+
assertPositionDiff('payee', positionPayeeB, positionPayeeC, {
|
|
495
|
+
pending: 0,
|
|
496
|
+
posted: -100
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
it('Aborts a forwarded payment.', async () => {
|
|
501
|
+
const transferId = '3000010'
|
|
502
|
+
const payment = ApiHelpers.buildPayment()
|
|
503
|
+
.deps(harness, TransferHandler)
|
|
504
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
505
|
+
.transferId(transferId)
|
|
506
|
+
.amount('100.00', 'BWP')
|
|
507
|
+
.expiry(120)
|
|
508
|
+
.build()
|
|
509
|
+
|
|
510
|
+
const [positionPayerA, positionPayeeA] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
511
|
+
await payment.prepare()
|
|
512
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
513
|
+
transferId: transferId,
|
|
514
|
+
proxyId: 'dfsp_a_proxy'
|
|
515
|
+
})
|
|
516
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
517
|
+
|
|
518
|
+
const [positionPayerB, positionPayeeB] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
519
|
+
assertPositionDiff('payer', positionPayerA, positionPayerB, {
|
|
520
|
+
pending: 0,
|
|
521
|
+
posted: 100
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
let transfer = await TransferFacade.getById(transferId)
|
|
525
|
+
assert.equal(transfer.transferState, 'RESERVED_FORWARDED')
|
|
526
|
+
|
|
527
|
+
await payment.abort()
|
|
528
|
+
transfer = await TransferFacade.getById(transferId)
|
|
529
|
+
assert.equal(transfer.transferState, 'ABORTED_ERROR')
|
|
530
|
+
|
|
531
|
+
const [positionPayerC, positionPayeeC] = await ApiHelpers.getPositions('dfsp_a', 'dfsp_b', 'BWP')
|
|
532
|
+
// A --> C is the same (Payment was aborted).
|
|
533
|
+
assertPositionDiff('payer', positionPayerA, positionPayerC, {
|
|
534
|
+
pending: 0,
|
|
535
|
+
posted: 0
|
|
536
|
+
})
|
|
537
|
+
assertPositionDiff('payee', positionPayeeB, positionPayeeC, {
|
|
538
|
+
pending: 0,
|
|
539
|
+
posted: 0
|
|
540
|
+
})
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
it('Notifies when the forwarded payment cannot be found.', async () => {
|
|
544
|
+
const transferId = '3000011'
|
|
545
|
+
|
|
546
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
547
|
+
transferId: transferId,
|
|
548
|
+
proxyId: 'dfsp_a_proxy'
|
|
549
|
+
})
|
|
550
|
+
let mark = harness.redpandaMark();
|
|
551
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
552
|
+
await harness.redpandaDrain(mark, 1)
|
|
553
|
+
|
|
554
|
+
const lastPayload = harness.spoolLastPayload(1)
|
|
555
|
+
Snapshot.from(`[
|
|
556
|
+
{
|
|
557
|
+
"errorInformation": {
|
|
558
|
+
"errorCode": "3200",
|
|
559
|
+
"errorDescription": "Generic ID not found - Forwarded transfer could not be found.",
|
|
560
|
+
"extensionList": {
|
|
561
|
+
"extension": [
|
|
562
|
+
{
|
|
563
|
+
"key": "cause",
|
|
564
|
+
"value": :ignore
|
|
565
|
+
}
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
]`).checkUnwrap(lastPayload)
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
it('Notifies if the transfer is in an invalid state.', async () => {
|
|
574
|
+
const transferId = '3000012'
|
|
575
|
+
const paymentA = ApiHelpers.buildPayment()
|
|
576
|
+
.deps(harness, TransferHandler)
|
|
577
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
578
|
+
.transferId(transferId)
|
|
579
|
+
.amount('100.00', 'BWP')
|
|
580
|
+
.expiry(1)
|
|
581
|
+
.build()
|
|
582
|
+
|
|
583
|
+
// Prepare and timeout.
|
|
584
|
+
await paymentA.prepare()
|
|
585
|
+
const transferBefore = await TransferFacade.getById(transferId)
|
|
586
|
+
assert.equal(transferBefore.transferState, 'RESERVED')
|
|
587
|
+
await sleepSeconds(10)
|
|
588
|
+
|
|
589
|
+
let mark = harness.redpandaMark()
|
|
590
|
+
await TimeoutHandler.timeout()
|
|
591
|
+
await harness.redpandaDrain(mark, 2)
|
|
592
|
+
|
|
593
|
+
const transferExpired = await TransferFacade.getById(transferId)
|
|
594
|
+
assert.equal(transferExpired.transferState, 'EXPIRED_RESERVED')
|
|
595
|
+
|
|
596
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
597
|
+
transferId: transferId,
|
|
598
|
+
proxyId: 'dfsp_a_proxy'
|
|
599
|
+
})
|
|
600
|
+
mark = harness.redpandaMark()
|
|
601
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
602
|
+
await harness.redpandaDrain(mark, 1)
|
|
603
|
+
|
|
604
|
+
Snapshot.from(`[
|
|
605
|
+
{
|
|
606
|
+
"errorInformation": {
|
|
607
|
+
"errorCode": "2001",
|
|
608
|
+
"errorDescription": "Internal server error - Invalid State: EXPIRED_RESERVED - expected: RESERVED",
|
|
609
|
+
"extensionList": {
|
|
610
|
+
"extension": [
|
|
611
|
+
{
|
|
612
|
+
"key": "cause",
|
|
613
|
+
"value": :ignore
|
|
614
|
+
}
|
|
615
|
+
]
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
]`).checkUnwrap(harness.spoolLastPayload(1))
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
it('Notifies with GET on timeout if the transfer is RESERVED_FORWARDED', async () => {
|
|
623
|
+
const transferId = '3000013'
|
|
624
|
+
const payment = ApiHelpers.buildPayment()
|
|
625
|
+
.deps(harness, TransferHandler)
|
|
626
|
+
.parties('dfsp_a', 'dfsp_b')
|
|
627
|
+
.transferId(transferId)
|
|
628
|
+
.amount('100.00', 'BWP')
|
|
629
|
+
.expiry(1)
|
|
630
|
+
.build()
|
|
631
|
+
|
|
632
|
+
// Prepare and forward.
|
|
633
|
+
await payment.prepare()
|
|
634
|
+
let transfer = await TransferFacade.getById(transferId)
|
|
635
|
+
assert.equal(transfer.transferState, 'RESERVED')
|
|
636
|
+
|
|
637
|
+
const forwardedMsg = ApiHelpers.buildMessageForwarded(harness, {
|
|
638
|
+
transferId: transferId,
|
|
639
|
+
proxyId: 'dfsp_a_proxy'
|
|
640
|
+
})
|
|
641
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
642
|
+
transfer = await TransferFacade.getById(transferId)
|
|
643
|
+
assert.equal(transfer.transferState, 'RESERVED_FORWARDED')
|
|
644
|
+
|
|
645
|
+
// Time out to trigger notification.
|
|
646
|
+
await sleepSeconds(10)
|
|
647
|
+
const mark = harness.redpandaMark()
|
|
648
|
+
const resultTimeout = await TimeoutHandler.timeout()
|
|
649
|
+
await harness.redpandaDrain(mark, 1)
|
|
650
|
+
|
|
651
|
+
assert(resultTimeout)
|
|
652
|
+
// Annoyingly the timeout handler still includes aborted transfers in timeouts.
|
|
653
|
+
const reservedForwardedTimedout = resultTimeout.transferForwardedList
|
|
654
|
+
.filter((transfer: any) => transfer.transferStateId === 'RESERVED_FORWARDED')
|
|
655
|
+
Snapshot.from(`[
|
|
656
|
+
{
|
|
657
|
+
"transferForwardedId": :ignore
|
|
658
|
+
"transferId": "3000013",
|
|
659
|
+
"expirationDate": :ignore
|
|
660
|
+
"attemptCount": 0,
|
|
661
|
+
"createdDate": :ignore
|
|
662
|
+
"transferStateId": "RESERVED_FORWARDED",
|
|
663
|
+
"payerParticipantCurrencyId": 5,
|
|
664
|
+
"payerFsp": "dfsp_a",
|
|
665
|
+
"payeeFsp": "dfsp_b",
|
|
666
|
+
"payeeParticipantCurrencyId": null,
|
|
667
|
+
"bulkTransferId": null,
|
|
668
|
+
"effectedParticipantCurrencyId": 5,
|
|
669
|
+
"externalPayerName": null,
|
|
670
|
+
"externalPayeeName": null
|
|
671
|
+
}
|
|
672
|
+
]`).checkUnwrap(reservedForwardedTimedout)
|
|
673
|
+
assert(reservedForwardedTimedout.length === 1)
|
|
674
|
+
// Get has no payload.
|
|
675
|
+
Snapshot.from(`[
|
|
676
|
+
{}
|
|
677
|
+
]`).checkUnwrap(harness.spoolLastPayload(1))
|
|
678
|
+
Snapshot.from(`[
|
|
679
|
+
"topic-notification-event"
|
|
680
|
+
]`).checkUnwrap(harness.spoolLastTopic(1))
|
|
681
|
+
|
|
682
|
+
await payment.abort()
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* FX + Proxy.
|
|
687
|
+
*/
|
|
688
|
+
|
|
689
|
+
it('should update fxTransfer internal state on prepare event fx-forwarded action', async () => {
|
|
690
|
+
const forex = ApiHelpers.buildForex()
|
|
691
|
+
.deps(harness, TransferHandler)
|
|
692
|
+
.commitRequestId('2000010')
|
|
693
|
+
.determiningTransferId('3000010')
|
|
694
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
695
|
+
.amountSource('100.00', 'BWP')
|
|
696
|
+
.amountTarget('1.00', 'USD')
|
|
697
|
+
.build()
|
|
698
|
+
|
|
699
|
+
await forex.prepare()
|
|
700
|
+
|
|
701
|
+
// Forward the fxPrepare to the proxy.
|
|
702
|
+
const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
703
|
+
commitRequestId: '2000010',
|
|
704
|
+
proxyId: 'dfsp_a_proxy'
|
|
705
|
+
})
|
|
706
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
707
|
+
|
|
708
|
+
// Check the result.
|
|
709
|
+
const transfer = await FxTransferService.getByIdLight('2000010')
|
|
710
|
+
assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
711
|
+
})
|
|
712
|
+
|
|
713
|
+
it('not timeout fxTransfer in RESERVED_FORWARDED internal transfer state', async () => {
|
|
714
|
+
const forex = ApiHelpers.buildForex()
|
|
715
|
+
.deps(harness, TransferHandler)
|
|
716
|
+
.commitRequestId('2000011')
|
|
717
|
+
.determiningTransferId('3000011')
|
|
718
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
719
|
+
.amountSource('100.00', 'BWP')
|
|
720
|
+
.amountTarget('1.00', 'USD')
|
|
721
|
+
.expiry(5)
|
|
722
|
+
.build()
|
|
723
|
+
|
|
724
|
+
await forex.prepare()
|
|
725
|
+
const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
726
|
+
commitRequestId: '2000011',
|
|
727
|
+
proxyId: 'dfsp_a_proxy'
|
|
728
|
+
})
|
|
729
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
730
|
+
let transfer = await FxTransferService.getByIdLight('2000011')
|
|
731
|
+
assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
732
|
+
|
|
733
|
+
// Now time it out - no message should be produced.
|
|
734
|
+
await sleepSeconds(10)
|
|
735
|
+
const mark = harness.redpandaMark()
|
|
736
|
+
await TimeoutHandler.timeout()
|
|
737
|
+
await harness.redpandaDrain(mark, 0)
|
|
738
|
+
|
|
739
|
+
// Check the result.
|
|
740
|
+
transfer = await FxTransferService.getByIdLight('2000011')
|
|
741
|
+
assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
// // This test is broken, there is currently no notification produced.
|
|
745
|
+
// // See note on `test/integration-override/handlers/transfers/handlers.test.js`.
|
|
746
|
+
// it.skip('produces a get notification if fx transfer stuck in RESERVED_FORWARDED', async () => {
|
|
747
|
+
// const forex = ApiHelpers.buildForex()
|
|
748
|
+
// .deps(harness, TransferHandler)
|
|
749
|
+
// .commitRequestId('2000012')
|
|
750
|
+
// .determiningTransferId('3000012')
|
|
751
|
+
// .parties('external_dfsp_a', 'external_dfsp_b')
|
|
752
|
+
// .amountSource('100.00', 'BWP')
|
|
753
|
+
// .amountTarget('1.00', 'USD')
|
|
754
|
+
// .expiry(5)
|
|
755
|
+
// .build()
|
|
756
|
+
|
|
757
|
+
// await forex.prepare()
|
|
758
|
+
// const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
759
|
+
// commitRequestId: '2000012',
|
|
760
|
+
// proxyId: 'dfsp_a_proxy'
|
|
761
|
+
// })
|
|
762
|
+
// await PrepareHandler.prepare(null, forwardedMsg)
|
|
763
|
+
// let transfer = await FxTransferService.getByIdLight('2000012')
|
|
764
|
+
// assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
765
|
+
|
|
766
|
+
// // Now time it out - no message should be produced.
|
|
767
|
+
// await sleepSeconds(10)
|
|
768
|
+
// const mark = harness.redpandaMark()
|
|
769
|
+
// await TimeoutHandler.timeout()
|
|
770
|
+
// await harness.redpandaDrain(mark, 0)
|
|
771
|
+
|
|
772
|
+
// // Check the result.
|
|
773
|
+
// transfer = await FxTransferService.getByIdLight('2000012')
|
|
774
|
+
// assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
775
|
+
// })
|
|
776
|
+
|
|
777
|
+
it('transitions RESERVED_FORWARDED -> RECEIVED_FULFIL_DEPENDENT on fx-fulfil', async () => {
|
|
778
|
+
const forex = ApiHelpers.buildForex()
|
|
779
|
+
.deps(harness, TransferHandler)
|
|
780
|
+
.commitRequestId('2000012')
|
|
781
|
+
.determiningTransferId('3000012')
|
|
782
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
783
|
+
.amountSource('100.00', 'BWP')
|
|
784
|
+
.amountTarget('1.00', 'USD')
|
|
785
|
+
.expiry(5)
|
|
786
|
+
.build()
|
|
787
|
+
await forex.prepare()
|
|
788
|
+
|
|
789
|
+
// Forward the message.
|
|
790
|
+
const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
791
|
+
commitRequestId: '2000012',
|
|
792
|
+
proxyId: 'dfsp_a_proxy'
|
|
793
|
+
})
|
|
794
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
795
|
+
let transfer = await FxTransferService.getByIdLight('2000012')
|
|
796
|
+
assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
797
|
+
|
|
798
|
+
await forex.fulfil()
|
|
799
|
+
transfer = await FxTransferService.getByIdLight('2000012')
|
|
800
|
+
assert.equal(transfer.fxTransferState, 'RECEIVED_FULFIL_DEPENDENT')
|
|
801
|
+
})
|
|
802
|
+
|
|
803
|
+
it('transitions RESERVED_FORWARDED -> ABORTED_ERROR on fx-fulfil-error', async () => {
|
|
804
|
+
const forex = ApiHelpers.buildForex()
|
|
805
|
+
.deps(harness, TransferHandler)
|
|
806
|
+
.commitRequestId('2000013')
|
|
807
|
+
.determiningTransferId('3000013')
|
|
808
|
+
.parties('external_dfsp_a', 'external_dfsp_b')
|
|
809
|
+
.amountSource('100.00', 'BWP')
|
|
810
|
+
.amountTarget('1.00', 'USD')
|
|
811
|
+
.expiry(5)
|
|
812
|
+
.build()
|
|
813
|
+
await forex.prepare()
|
|
814
|
+
|
|
815
|
+
// Forward the message.
|
|
816
|
+
const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
817
|
+
commitRequestId: '2000013',
|
|
818
|
+
proxyId: 'dfsp_a_proxy'
|
|
819
|
+
})
|
|
820
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
821
|
+
let transfer = await FxTransferService.getByIdLight('2000013')
|
|
822
|
+
assert.equal(transfer.fxTransferState, 'RESERVED_FORWARDED')
|
|
823
|
+
|
|
824
|
+
await forex.abort()
|
|
825
|
+
transfer = await FxTransferService.getByIdLight('2000013')
|
|
826
|
+
assert.equal(transfer.fxTransferState, 'ABORTED_ERROR')
|
|
827
|
+
})
|
|
828
|
+
|
|
829
|
+
it('notifies if fxTransfer is not found', async () => {
|
|
830
|
+
// Forward the message.
|
|
831
|
+
const forwardedMsg = ApiHelpers.buildMessageForwardedFx(harness, {
|
|
832
|
+
commitRequestId: '2000014',
|
|
833
|
+
proxyId: 'dfsp_a_proxy'
|
|
834
|
+
})
|
|
835
|
+
const mark = harness.redpandaMark()
|
|
836
|
+
await PrepareHandler.prepare(null, forwardedMsg)
|
|
837
|
+
await harness.redpandaDrain(mark, 1)
|
|
838
|
+
|
|
839
|
+
Snapshot.from(`[
|
|
840
|
+
{
|
|
841
|
+
"errorInformation": {
|
|
842
|
+
"errorCode": "3200",
|
|
843
|
+
"errorDescription": "Generic ID not found - Forwarded fxTransfer could not be found.",
|
|
844
|
+
"extensionList": {
|
|
845
|
+
"extension": [
|
|
846
|
+
{
|
|
847
|
+
"key": "cause",
|
|
848
|
+
"value": "FSPIOPError: Forwarded fxTransfer could not be found.:ignore
|
|
849
|
+
}
|
|
850
|
+
]
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
]`).checkUnwrap(harness.spoolLastPayload(1))
|
|
855
|
+
Snapshot.from(`[
|
|
856
|
+
"topic-notification-event"
|
|
857
|
+
]`).checkUnwrap(harness.spoolLastTopic(1))
|
|
858
|
+
})
|
|
859
|
+
|
|
860
|
+
// // TODO: I'm not sure how to get the timeouts to emit the position change messages.
|
|
861
|
+
// // I think this was actually broken in the original tests.
|
|
862
|
+
// it.skip('notifies if the transfer is in an invalid state', async () => {
|
|
863
|
+
// // Create a forex, time it out, but the problem is the timeouts are broken?
|
|
864
|
+
// const forex = ApiHelpers.buildForex()
|
|
865
|
+
// .deps(harness, TransferHandler)
|
|
866
|
+
// .commitRequestId('2000015')
|
|
867
|
+
// .determiningTransferId('3000015')
|
|
868
|
+
// .parties('external_dfsp_a', 'external_dfsp_b')
|
|
869
|
+
// .amountSource('100.00', 'BWP')
|
|
870
|
+
// .amountTarget('1.00', 'USD')
|
|
871
|
+
// .expiry(1)
|
|
872
|
+
// .build()
|
|
873
|
+
// await forex.prepare()
|
|
874
|
+
|
|
875
|
+
// await sleepSeconds(5)
|
|
876
|
+
|
|
877
|
+
// const knex = Db.getKnex()
|
|
878
|
+
// await knex('segment')
|
|
879
|
+
// .where({ tableName: 'fxTransferStateChange', segmentType: 'timeout' })
|
|
880
|
+
// .update({ value: 0 })
|
|
881
|
+
|
|
882
|
+
// const mark = harness.redpandaMark()
|
|
883
|
+
// // For some reason, timeout isn't producing any messages.
|
|
884
|
+
// const timeoutResult = await TimeoutHandler.timeout()
|
|
885
|
+
// await harness.redpandaDrain(mark, 1)
|
|
886
|
+
|
|
887
|
+
// let fxTransfer = await FxTransferService.getByIdLight('2000015')
|
|
888
|
+
// assert.equal(fxTransfer.fxTransferState, 'EXPIRED_RESERVED')
|
|
889
|
+
// })
|
|
890
|
+
|
|
891
|
+
it(
|
|
892
|
+
`Scheme A: POST /fxTransfer call I.e. Debtor: Payer DFSP → Creditor: Proxy AR
|
|
893
|
+
Payer DFSP position account must be updated (reserved)`,
|
|
894
|
+
async () => {
|
|
895
|
+
const externalFxp = 'external_fxp'
|
|
896
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalFxp, 'fxp_proxy')
|
|
897
|
+
|
|
898
|
+
const positionPayerPre = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
899
|
+
const forex = ApiHelpers.buildForex()
|
|
900
|
+
.deps(harness, TransferHandler)
|
|
901
|
+
.commitRequestId('2000017')
|
|
902
|
+
.determiningTransferId('3000017')
|
|
903
|
+
.parties('dfsp_a', 'external_dfsp_b')
|
|
904
|
+
.amountSource('100.00', 'BWP')
|
|
905
|
+
.amountTarget('1.00', 'USD')
|
|
906
|
+
.build()
|
|
907
|
+
|
|
908
|
+
await forex.prepare()
|
|
909
|
+
const fxTransfer = await FxTransferService.getByIdLight('2000017')
|
|
910
|
+
assert.equal(fxTransfer.fxTransferState, 'RESERVED')
|
|
911
|
+
|
|
912
|
+
const positionPayerPost = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
913
|
+
assertPositionDiff('payer', positionPayerPre, positionPayerPost, {
|
|
914
|
+
pending: 0,
|
|
915
|
+
posted: 100
|
|
916
|
+
})
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
it(
|
|
920
|
+
`Scheme A: POST /Transfer call I.e. Debtor: Proxy AR → Creditor: Proxy AR
|
|
921
|
+
Do nothing (produce message with key 0)`,
|
|
922
|
+
async () => {
|
|
923
|
+
const externalFxp = 'external_fxp'
|
|
924
|
+
const externalPayee = 'external_payee'
|
|
925
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalFxp, 'dfsp_a_proxy')
|
|
926
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayee, 'dfsp_a_proxy')
|
|
927
|
+
|
|
928
|
+
const positionPayerPre = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
929
|
+
const forex = ApiHelpers.buildForex()
|
|
930
|
+
.deps(harness, TransferHandler)
|
|
931
|
+
.commitRequestId('2000018')
|
|
932
|
+
.determiningTransferId('3000018')
|
|
933
|
+
.parties('dfsp_a', externalFxp)
|
|
934
|
+
.amountSource('100.00', 'BWP')
|
|
935
|
+
.amountTarget('10.00', 'USD')
|
|
936
|
+
.build()
|
|
937
|
+
await forex.prepareAndFulfil()
|
|
938
|
+
|
|
939
|
+
const positionPayerAfterFx = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
940
|
+
assertPositionDiff('payer', positionPayerPre, positionPayerAfterFx, {
|
|
941
|
+
pending: 0,
|
|
942
|
+
posted: 100
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
// Send the transfer through.
|
|
946
|
+
const payment = ApiHelpers.buildPayment()
|
|
947
|
+
.deps(harness, TransferHandler)
|
|
948
|
+
.parties(externalFxp, externalPayee)
|
|
949
|
+
.transferId('3000018')
|
|
950
|
+
.amount('10.00', 'USD')
|
|
951
|
+
.fx()
|
|
952
|
+
.build()
|
|
953
|
+
await payment.prepare()
|
|
954
|
+
|
|
955
|
+
const positionPayerAfterTransfer = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
956
|
+
// Ensure position doesn't change.
|
|
957
|
+
assertPositionDiff('payer', positionPayerAfterFx, positionPayerAfterTransfer, {
|
|
958
|
+
pending: 0,
|
|
959
|
+
posted: 0
|
|
960
|
+
})
|
|
961
|
+
})
|
|
962
|
+
|
|
963
|
+
it(
|
|
964
|
+
`Scheme R: POST /fxTransfer call I.e. Debtor: Proxy AR → Creditor: FXP
|
|
965
|
+
Proxy AR position account in source currency must be updated (reserved)`,
|
|
966
|
+
async () => {
|
|
967
|
+
const externalPayer = 'external_payer_r'
|
|
968
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
969
|
+
|
|
970
|
+
const positionProxyPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
971
|
+
const forex = ApiHelpers.buildForex()
|
|
972
|
+
.deps(harness, TransferHandler)
|
|
973
|
+
.commitRequestId('2000019')
|
|
974
|
+
.determiningTransferId('3000019')
|
|
975
|
+
.parties(externalPayer, 'dfsp_b')
|
|
976
|
+
.amountSource('100.00', 'BWP')
|
|
977
|
+
.amountTarget('10.00', 'USD')
|
|
978
|
+
.build()
|
|
979
|
+
await forex.prepare()
|
|
980
|
+
const positionProxyPost = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
981
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyPost, {
|
|
982
|
+
pending: 0,
|
|
983
|
+
posted: 100
|
|
984
|
+
})
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
it(
|
|
988
|
+
`Scheme R: POST /transfer call I.e. Debtor: FXP → Creditor: Proxy RB
|
|
989
|
+
FXP position account in targeted currency must be updated (reserved)`,
|
|
990
|
+
async () => {
|
|
991
|
+
const externalPayer = 'external_payer_r'
|
|
992
|
+
const externalPayee = 'external_payee_r'
|
|
993
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
994
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayee, 'dfsp_b_proxy')
|
|
995
|
+
|
|
996
|
+
await ApiHelpers.buildForex()
|
|
997
|
+
.deps(harness, TransferHandler)
|
|
998
|
+
.commitRequestId('2000020')
|
|
999
|
+
.determiningTransferId('3000020')
|
|
1000
|
+
.parties(externalPayer, 'fxp_a')
|
|
1001
|
+
.amountSource('100.00', 'BWP')
|
|
1002
|
+
.amountTarget('10.00', 'USD')
|
|
1003
|
+
.build()
|
|
1004
|
+
.prepareAndFulfil()
|
|
1005
|
+
|
|
1006
|
+
const positionFxpPre = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1007
|
+
const payment = ApiHelpers.buildPayment()
|
|
1008
|
+
.deps(harness, TransferHandler)
|
|
1009
|
+
.parties(externalPayer, externalPayee)
|
|
1010
|
+
.transferId('3000020')
|
|
1011
|
+
.amount('10.00', 'USD')
|
|
1012
|
+
.fx()
|
|
1013
|
+
.build()
|
|
1014
|
+
|
|
1015
|
+
await payment.prepare()
|
|
1016
|
+
|
|
1017
|
+
const positionFxpPost = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1018
|
+
assertPositionDiff('payer', positionFxpPre, positionFxpPost, {
|
|
1019
|
+
pending: 0,
|
|
1020
|
+
posted: 10
|
|
1021
|
+
})
|
|
1022
|
+
})
|
|
1023
|
+
|
|
1024
|
+
it(
|
|
1025
|
+
`Scheme B: POST /transfer call I.e. Debtor: Proxy RB → Creditor: Payee DFSP
|
|
1026
|
+
Proxy RB position account must be updated (reserved)`,
|
|
1027
|
+
async () => {
|
|
1028
|
+
const externalPayer = 'external_payer_scheme_b'
|
|
1029
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
1030
|
+
|
|
1031
|
+
const positionProxyPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1032
|
+
const payment = ApiHelpers.buildPayment()
|
|
1033
|
+
.deps(harness, TransferHandler)
|
|
1034
|
+
.parties(externalPayer, 'dfsp_b')
|
|
1035
|
+
.transferId('3000021')
|
|
1036
|
+
.amount('93.00', 'BWP')
|
|
1037
|
+
.build()
|
|
1038
|
+
|
|
1039
|
+
await payment.prepare()
|
|
1040
|
+
|
|
1041
|
+
const positionProxyPost = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1042
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyPost, {
|
|
1043
|
+
pending: 0,
|
|
1044
|
+
posted: 93
|
|
1045
|
+
})
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
it(
|
|
1049
|
+
`Scheme B: PUT /transfers call I.e. From: Payee DFSP → To: Proxy RB
|
|
1050
|
+
Payee DFSP position account must be updated`,
|
|
1051
|
+
async () => {
|
|
1052
|
+
const externalPayer = 'external_payer_scheme'
|
|
1053
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_b_proxy')
|
|
1054
|
+
|
|
1055
|
+
const [positionProxyPre, positionPayeePre] = await ApiHelpers.getPositions(
|
|
1056
|
+
'dfsp_b_proxy', 'dfsp_a', 'BWP'
|
|
1057
|
+
)
|
|
1058
|
+
const payment = ApiHelpers.buildPayment()
|
|
1059
|
+
.deps(harness, TransferHandler)
|
|
1060
|
+
.parties(externalPayer, 'dfsp_a')
|
|
1061
|
+
.transferId('3000022')
|
|
1062
|
+
.amount('38.92', 'BWP')
|
|
1063
|
+
.build()
|
|
1064
|
+
await payment.prepare()
|
|
1065
|
+
const [positionProxyAfterPrepare, positionPayeeAfterPrepare] = await ApiHelpers.getPositions(
|
|
1066
|
+
'dfsp_b_proxy', 'dfsp_a', 'BWP'
|
|
1067
|
+
)
|
|
1068
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyAfterPrepare, {
|
|
1069
|
+
posted: 38.92
|
|
1070
|
+
})
|
|
1071
|
+
|
|
1072
|
+
await payment.fulfil()
|
|
1073
|
+
const [positionProxyAfterFulfil, positionPayeeAfterFulfil] = await ApiHelpers.getPositions(
|
|
1074
|
+
'dfsp_b_proxy', 'dfsp_a', 'BWP'
|
|
1075
|
+
)
|
|
1076
|
+
assertPositionDiff('payee', positionPayeeAfterPrepare, positionPayeeAfterFulfil, {
|
|
1077
|
+
posted: -38.92
|
|
1078
|
+
})
|
|
1079
|
+
})
|
|
1080
|
+
|
|
1081
|
+
it(
|
|
1082
|
+
`Scheme R: PUT /transfers call I.e. From: Proxy RB → To: Proxy AR
|
|
1083
|
+
If it is a normal transfer without currency conversion
|
|
1084
|
+
ProxyRB account must be updated`,
|
|
1085
|
+
async () => {
|
|
1086
|
+
const externalPayer = 'external_payer_scheme_r'
|
|
1087
|
+
const externalPayee = 'external_payee_scheme_r'
|
|
1088
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
1089
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayee, 'dfsp_b_proxy')
|
|
1090
|
+
const [positionProxyAPre, positionProxyBPre] = await ApiHelpers.getPositions(
|
|
1091
|
+
'dfsp_a_proxy', 'dfsp_b_proxy', 'BWP'
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
// Both parties are external.
|
|
1095
|
+
const payment = ApiHelpers.buildPayment()
|
|
1096
|
+
.deps(harness, TransferHandler)
|
|
1097
|
+
.parties(externalPayer, externalPayee)
|
|
1098
|
+
.transferId('3000023')
|
|
1099
|
+
.amount('12.05', 'BWP')
|
|
1100
|
+
.build()
|
|
1101
|
+
await payment.prepare()
|
|
1102
|
+
const [positionProxyAPostPrepare] = await ApiHelpers.getPositions(
|
|
1103
|
+
'dfsp_a_proxy', 'dfsp_b_proxy', 'BWP'
|
|
1104
|
+
)
|
|
1105
|
+
assertPositionDiff('payer', positionProxyAPre, positionProxyAPostPrepare, {
|
|
1106
|
+
posted: 12.05
|
|
1107
|
+
})
|
|
1108
|
+
|
|
1109
|
+
await payment.fulfil()
|
|
1110
|
+
const [_, positionProxyBPostFulfil] = await ApiHelpers.getPositions(
|
|
1111
|
+
'dfsp_a_proxy', 'dfsp_b_proxy', 'BWP'
|
|
1112
|
+
)
|
|
1113
|
+
assertPositionDiff('payee', positionProxyBPre, positionProxyBPostFulfil, {
|
|
1114
|
+
posted: -12.05
|
|
1115
|
+
})
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
it(
|
|
1119
|
+
`Scheme R: PUT /fxTransfer call I.e. From: FXP → To: Proxy AR
|
|
1120
|
+
No position changes should happen`,
|
|
1121
|
+
async () => {
|
|
1122
|
+
const externalPayer = 'external_payer_scheme_r'
|
|
1123
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
1124
|
+
const positionProxyPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1125
|
+
const positionFxpPreBWP = await ApiHelpers.getPositionAccount('fxp_a', 'BWP')
|
|
1126
|
+
const positionFxpPreUSD = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1127
|
+
|
|
1128
|
+
const forex = ApiHelpers.buildForex()
|
|
1129
|
+
.deps(harness, TransferHandler)
|
|
1130
|
+
.commitRequestId('2000030')
|
|
1131
|
+
.determiningTransferId('3000030')
|
|
1132
|
+
.parties(externalPayer, 'fxp_a')
|
|
1133
|
+
.amountSource('100.00', 'BWP')
|
|
1134
|
+
.amountTarget('10.00', 'USD')
|
|
1135
|
+
.build()
|
|
1136
|
+
|
|
1137
|
+
await forex.prepare()
|
|
1138
|
+
const positionProxyAfterPrepare = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1139
|
+
|
|
1140
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyAfterPrepare, {
|
|
1141
|
+
posted: 100
|
|
1142
|
+
})
|
|
1143
|
+
|
|
1144
|
+
await forex.fulfil()
|
|
1145
|
+
|
|
1146
|
+
// Fxp positions don't change until the payment gets processed.
|
|
1147
|
+
const positionFxpAfterFulfilBWP = await ApiHelpers.getPositionAccount('fxp_a', 'BWP')
|
|
1148
|
+
const positionFxpAfterFulfilUSD = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1149
|
+
assertPositionDiff('payer', positionFxpPreBWP, positionFxpAfterFulfilBWP, { posted: 0 })
|
|
1150
|
+
assertPositionDiff('payee', positionFxpPreUSD, positionFxpAfterFulfilUSD, { posted: 0 })
|
|
1151
|
+
})
|
|
1152
|
+
|
|
1153
|
+
it(
|
|
1154
|
+
`Scheme R: PUT /fxTransfer call I.e. From: FXP → To: Proxy AR
|
|
1155
|
+
with wrong headers - ABORT VALIDATION`,
|
|
1156
|
+
async () => {
|
|
1157
|
+
const externalPayer = 'external_payer_scheme_r'
|
|
1158
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
1159
|
+
const positionProxyPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1160
|
+
|
|
1161
|
+
const forex = ApiHelpers.buildForex()
|
|
1162
|
+
.deps(harness, TransferHandler)
|
|
1163
|
+
.commitRequestId('2000121')
|
|
1164
|
+
.determiningTransferId('3000121')
|
|
1165
|
+
.parties(externalPayer, 'fxp_a')
|
|
1166
|
+
.amountSource('100.00', 'BWP')
|
|
1167
|
+
.amountTarget('10.00', 'USD')
|
|
1168
|
+
.build()
|
|
1169
|
+
|
|
1170
|
+
await forex.prepare()
|
|
1171
|
+
const positionProxyAfterPrepare = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1172
|
+
|
|
1173
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyAfterPrepare, {
|
|
1174
|
+
posted: 100
|
|
1175
|
+
})
|
|
1176
|
+
|
|
1177
|
+
// Manually fulfil with bad headers.
|
|
1178
|
+
const messageFulfil = forex.buildMessageFulfil()
|
|
1179
|
+
messageFulfil.value.content.headers['fspiop-source'] = 'wrongfsp'
|
|
1180
|
+
const mark = harness.redpandaMark()
|
|
1181
|
+
await TransferHandler.fulfil(null, messageFulfil)
|
|
1182
|
+
await harness.redpandaDrain(mark, 2) // maybe 1?
|
|
1183
|
+
|
|
1184
|
+
const fxTransfer = await FxTransferService.getByIdLight('2000121')
|
|
1185
|
+
assert.equal(fxTransfer.fxTransferState, 'ABORTED_ERROR')
|
|
1186
|
+
|
|
1187
|
+
// Position is reverted.
|
|
1188
|
+
const positionProxyAfterAbort = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1189
|
+
assertPositionDiff('payer', positionProxyPre, positionProxyAfterAbort, {
|
|
1190
|
+
posted: 0
|
|
1191
|
+
})
|
|
1192
|
+
})
|
|
1193
|
+
|
|
1194
|
+
it(
|
|
1195
|
+
`Scheme R: PUT /transfers call I.e. From: Proxy RB → To: Proxy AR
|
|
1196
|
+
If it is a FX transfer with currency conversion
|
|
1197
|
+
FXP and ProxyRB account must be updated`,
|
|
1198
|
+
async () => {
|
|
1199
|
+
const externalPayer = 'external_payer_scheme_r2'
|
|
1200
|
+
const externalPayee = 'external_payee_scheme_r2'
|
|
1201
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayer, 'dfsp_a_proxy')
|
|
1202
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayee, 'dfsp_b_proxy')
|
|
1203
|
+
|
|
1204
|
+
const positionProxyPayerPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1205
|
+
const positionFxpPreBWP = await ApiHelpers.getPositionAccount('fxp_a', 'BWP')
|
|
1206
|
+
const positionFxpPreUSD = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1207
|
+
const positionProxyPayeePre = await ApiHelpers.getPositionAccount('dfsp_b_proxy', 'USD')
|
|
1208
|
+
|
|
1209
|
+
const forex = ApiHelpers.buildForex()
|
|
1210
|
+
.deps(harness, TransferHandler)
|
|
1211
|
+
.commitRequestId('2000022')
|
|
1212
|
+
.determiningTransferId('3000222')
|
|
1213
|
+
.parties(externalPayer, 'fxp_a')
|
|
1214
|
+
.amountSource('100.00', 'BWP')
|
|
1215
|
+
.amountTarget('10.00', 'USD')
|
|
1216
|
+
.build()
|
|
1217
|
+
await forex.prepare()
|
|
1218
|
+
await forex.fulfil()
|
|
1219
|
+
|
|
1220
|
+
const payment = ApiHelpers.buildPayment()
|
|
1221
|
+
.deps(harness, TransferHandler)
|
|
1222
|
+
.parties(externalPayer, externalPayee)
|
|
1223
|
+
.transferId('3000222')
|
|
1224
|
+
.amount('10.00', 'USD')
|
|
1225
|
+
.fx()
|
|
1226
|
+
.build()
|
|
1227
|
+
await payment.prepareAndFulfil()
|
|
1228
|
+
|
|
1229
|
+
const positionProxyPayerPost = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1230
|
+
const positionFxpPostBWP = await ApiHelpers.getPositionAccount('fxp_a', 'BWP')
|
|
1231
|
+
const positionFxpPostUSD = await ApiHelpers.getPositionAccount('fxp_a', 'USD')
|
|
1232
|
+
const positionProxyPayeePost = await ApiHelpers.getPositionAccount('dfsp_b_proxy', 'USD')
|
|
1233
|
+
|
|
1234
|
+
assertPositionDiff('payer', positionProxyPayerPre, positionProxyPayerPost, { posted: 100 })
|
|
1235
|
+
assertPositionDiff('payer', positionFxpPreBWP, positionFxpPostBWP, { posted: -100 })
|
|
1236
|
+
assertPositionDiff('payer', positionFxpPreUSD, positionFxpPostUSD, { posted: 10 })
|
|
1237
|
+
assertPositionDiff('payee', positionProxyPayeePre, positionProxyPayeePost, { posted: -10 })
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1240
|
+
|
|
1241
|
+
it(
|
|
1242
|
+
`Scheme A: PUT /transfers call I.e. From: Proxy AR → To: Payer FSP
|
|
1243
|
+
If it is a FX transfer with currency conversion
|
|
1244
|
+
PayerFSP and ProxyAR account must be updated`,
|
|
1245
|
+
async () => {
|
|
1246
|
+
const externalFxp = 'external_fxp_schema_a'
|
|
1247
|
+
const externalPayee = 'external_payee_scheme_a'
|
|
1248
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalFxp, 'dfsp_a_proxy')
|
|
1249
|
+
await proxyCache.getCache().addDfspIdToProxyMapping(externalPayee, 'dfsp_a_proxy')
|
|
1250
|
+
|
|
1251
|
+
const positionPayerPre = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
1252
|
+
const positionProxyPre = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1253
|
+
|
|
1254
|
+
// Set up Forex from in scheme payer to external Fxp.
|
|
1255
|
+
const forex = ApiHelpers.buildForex()
|
|
1256
|
+
.deps(harness, TransferHandler)
|
|
1257
|
+
.commitRequestId('2000033')
|
|
1258
|
+
.determiningTransferId('3000033')
|
|
1259
|
+
.parties('dfsp_a', externalFxp)
|
|
1260
|
+
.amountSource('100.00', 'BWP')
|
|
1261
|
+
.amountTarget('10.00', 'USD')
|
|
1262
|
+
.build()
|
|
1263
|
+
await forex.prepare()
|
|
1264
|
+
|
|
1265
|
+
const positionPayerPostFxPrepare = await ApiHelpers.getPositionAccount('dfsp_a', 'BWP')
|
|
1266
|
+
assertPositionDiff('payer', positionPayerPre, positionPayerPostFxPrepare, { posted: 100 })
|
|
1267
|
+
|
|
1268
|
+
await forex.fulfil()
|
|
1269
|
+
|
|
1270
|
+
const payment = ApiHelpers.buildPayment()
|
|
1271
|
+
.deps(harness, TransferHandler)
|
|
1272
|
+
.parties('dfsp_a', externalPayee)
|
|
1273
|
+
.transferId('3000033')
|
|
1274
|
+
.amount('10.00', 'USD')
|
|
1275
|
+
.fx()
|
|
1276
|
+
.build()
|
|
1277
|
+
await payment.prepareAndFulfil()
|
|
1278
|
+
|
|
1279
|
+
const positionProxyPost = await ApiHelpers.getPositionAccount('dfsp_a_proxy', 'BWP')
|
|
1280
|
+
assertPositionDiff('payee', positionProxyPre, positionProxyPost, { posted: -100 })
|
|
1281
|
+
}
|
|
1282
|
+
)
|
|
1283
|
+
})
|