@mojaloop/central-ledger 17.7.8 → 17.8.0-orb.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ncurc.yaml +3 -1
- package/.nvmrc +1 -1
- package/.nycrc.yml +16 -1
- package/Dockerfile +9 -10
- package/README.md +39 -9
- package/audit-ci.jsonc +15 -2
- package/config/default.json +17 -1
- package/docker/central-ledger/default.json +9 -0
- package/docker/config-modifier/configs/central-ledger.js +25 -0
- package/docker/env.sh +15 -0
- package/docker/kafka/scripts/provision.sh +4 -1
- package/docker/ml-api-adapter/default.json +4 -1
- package/docker-compose.yml +90 -2
- package/documentation/db/erd-transfer-timeout.png +0 -0
- package/documentation/db/erd-transfer-timeout.txt +81 -0
- package/documentation/fx-implementation/README.md +48 -0
- package/documentation/fx-implementation/assets/fx-position-movements.drawio.svg +4 -0
- package/documentation/fx-implementation/assets/test-scenario.drawio.svg +4 -0
- package/documentation/sequence-diagrams/Handler - FX timeout.plantuml +123 -0
- package/documentation/sequence-diagrams/Handler - FX timeout.png +0 -0
- package/documentation/sequence-diagrams/Handler - timeout.plantuml +81 -0
- package/documentation/sequence-diagrams/Handler - timeout.png +0 -0
- package/documentation/state-diagrams/transfer-ML-spec-states-diagram.png +0 -0
- package/documentation/state-diagrams/transfer-internal-states-diagram.png +0 -0
- package/documentation/state-diagrams/transfer-internal-states.plantuml +75 -0
- package/documentation/state-diagrams/transfer-states.plantuml +13 -0
- package/migrations/310204_transferParticipant-participantId.js +52 -0
- package/migrations/310403_participantPositionChange-participantCurrencyId.js +47 -0
- package/migrations/310404_participantPositionChange-change.js +46 -0
- package/migrations/600010_fxTransferType.js +43 -0
- package/migrations/600011_fxTransferType-indexes.js +38 -0
- package/migrations/600012_fxParticipantCurrencyType.js +43 -0
- package/migrations/600013_fxParticipantCurrencyType-indexes.js +38 -0
- package/migrations/600100_fxTransferDuplicateCheck.js +42 -0
- package/migrations/600110_fxTransferErrorDuplicateCheck.js.js +17 -0
- package/migrations/600200_fxTransfer.js +51 -0
- package/migrations/600201_fxTransfer-indexes.js +40 -0
- package/migrations/600400_fxTransferStateChange.js +46 -0
- package/migrations/600401_fxTransferStateChange-indexes.js +40 -0
- package/migrations/600501_fxWatchList.js +46 -0
- package/migrations/600502_fxWatchList-indexes.js +40 -0
- package/migrations/600600_fxTransferFulfilmentDuplicateCheck.js +43 -0
- package/migrations/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +38 -0
- package/migrations/600700_fxTransferFulfilment.js +47 -0
- package/migrations/600701_fxTransferFulfilment-indexes.js +43 -0
- package/migrations/600800_fxTransferExtension.js +47 -0
- package/migrations/601400_fxTransferTimeout.js +43 -0
- package/migrations/601401_fxTransferTimeout-indexes.js +37 -0
- package/migrations/601500_fxTransferError.js +44 -0
- package/migrations/601501_fxTransferError-indexes.js +37 -0
- package/migrations/610200_fxTransferParticipant.js +52 -0
- package/migrations/610201_fxTransferParticipant-indexes.js +44 -0
- package/migrations/610202_fxTransferParticipant-participantId.js +52 -0
- package/migrations/610403_participantPositionChange-fxTransfer.js +46 -0
- package/migrations/910101_feature904DataMigration.js +46 -52
- package/migrations/910102_feature949DataMigration.js +219 -225
- package/migrations/950104_settlementModel-settlementAccountTypeId.js +15 -20
- package/migrations/950108_participantProxy.js +18 -0
- package/migrations/950109_fxQuote.js +19 -0
- package/migrations/950110_fxQuoteResponse.js +25 -0
- package/migrations/950111_fxQuoteError.js +23 -0
- package/migrations/950113_fxQuoteDuplicateCheck.js +18 -0
- package/migrations/950114_fxQuoteResponseDuplicateCheck.js +21 -0
- package/migrations/950115_fxQuoteConversionTerms.js +36 -0
- package/migrations/950116_fxQuoteConversionTermsExtension.js +21 -0
- package/migrations/950117_fxQuoteResponseConversionTerms.js +39 -0
- package/migrations/950118_fxQuoteResponseConversionTermsExtension.js +21 -0
- package/migrations/950119_fxCharge.js +27 -0
- package/migrations/960100_create_externalParticipant.js +47 -0
- package/migrations/960110_alter_transferParticipant__addFiled_externalParticipantId.js +50 -0
- package/migrations/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +50 -0
- package/package.json +27 -20
- package/seeds/endpointType.js +18 -0
- package/seeds/fxParticipantCurrencyType.js +45 -0
- package/seeds/fxTransferType.js +45 -0
- package/seeds/participant.js +2 -1
- package/seeds/transferParticipantRoleType.js +9 -0
- package/seeds/transferState.js +10 -0
- package/src/api/interface/swagger.json +23 -30
- package/src/api/participants/handler.js +6 -2
- package/src/api/participants/routes.js +8 -7
- package/src/api/root/handler.js +15 -5
- package/src/domain/fx/cyril.js +466 -0
- package/src/domain/fx/index.js +107 -0
- package/src/domain/participant/index.js +108 -1
- package/src/domain/position/abort.js +215 -0
- package/src/domain/position/binProcessor.js +361 -99
- package/src/domain/position/fulfil.js +252 -111
- package/src/domain/position/fx-fulfil.js +138 -0
- package/src/domain/position/fx-prepare.js +280 -0
- package/src/domain/position/fx-timeout-reserved.js +159 -0
- package/src/domain/position/index.js +1 -0
- package/src/domain/position/prepare.js +69 -49
- package/src/domain/position/timeout-reserved.js +162 -0
- package/src/domain/timeout/index.js +26 -2
- package/src/domain/transfer/index.js +22 -5
- package/src/domain/transfer/transform.js +19 -6
- package/src/handlers/admin/handler.js +0 -2
- package/src/handlers/bulk/fulfil/handler.js +5 -5
- package/src/handlers/bulk/get/handler.js +5 -5
- package/src/handlers/bulk/prepare/handler.js +9 -9
- package/src/handlers/bulk/processing/handler.js +8 -7
- package/src/handlers/bulk/shared/validator.js +1 -1
- package/src/handlers/positions/handler.js +23 -10
- package/src/handlers/positions/handlerBatch.js +54 -26
- package/src/handlers/register.js +2 -1
- package/src/handlers/timeouts/handler.js +212 -68
- package/src/handlers/transfers/FxFulfilService.js +387 -0
- package/src/handlers/transfers/createRemittanceEntity.js +106 -0
- package/src/handlers/transfers/dto.js +53 -0
- package/src/handlers/transfers/handler.js +609 -571
- package/src/handlers/transfers/prepare.js +572 -0
- package/src/handlers/transfers/validator.js +75 -14
- package/src/lib/cache.js +1 -1
- package/src/lib/config.js +6 -3
- package/src/lib/healthCheck/subServiceHealth.js +12 -2
- package/src/lib/proxyCache.js +131 -0
- package/src/models/bulkTransfer/facade.js +58 -82
- package/src/models/fxTransfer/duplicateCheck.js +153 -0
- package/src/models/fxTransfer/fxTransfer.js +578 -0
- package/src/models/fxTransfer/fxTransferError.js +53 -0
- package/src/models/fxTransfer/fxTransferExtension.js +41 -0
- package/src/models/fxTransfer/fxTransferTimeout.js +68 -0
- package/src/models/fxTransfer/index.js +15 -0
- package/src/models/fxTransfer/stateChange.js +47 -0
- package/src/models/fxTransfer/watchList.js +49 -0
- package/src/models/ledgerAccountType/ledgerAccountType.js +8 -32
- package/src/models/misc/segment.js +0 -1
- package/src/models/participant/externalParticipant.js +96 -0
- package/src/models/participant/externalParticipantCached.js +148 -0
- package/src/models/participant/facade.js +192 -123
- package/src/models/participant/participant.js +2 -1
- package/src/models/participant/participantCurrency.js +1 -1
- package/src/models/participant/participantPosition.js +2 -8
- package/src/models/position/batch.js +90 -1
- package/src/models/position/facade.js +5 -5
- package/src/models/position/participantPositionChanges.js +68 -0
- package/src/models/settlement/settlementModel.js +5 -23
- package/src/models/transfer/facade.js +806 -447
- package/src/shared/constants.js +52 -0
- package/src/shared/fspiopErrorFactory.js +131 -0
- package/src/shared/logger/index.js +8 -0
- package/src/shared/loggingPlugin.js +43 -0
- package/src/shared/plugins.js +6 -0
- package/src/shared/setup.js +10 -0
- package/test-integration.Dockerfile +1 -1
- package/test.Dockerfile +1 -1
|
@@ -28,17 +28,20 @@
|
|
|
28
28
|
* @module src/models/participant/facade/
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
const Db = require('../../lib/db')
|
|
32
31
|
const Time = require('@mojaloop/central-services-shared').Util.Time
|
|
32
|
+
const { Enum } = require('@mojaloop/central-services-shared')
|
|
33
33
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
34
34
|
const Metrics = require('@mojaloop/central-services-metrics')
|
|
35
|
+
|
|
36
|
+
const Db = require('../../lib/db')
|
|
35
37
|
const Cache = require('../../lib/cache')
|
|
36
38
|
const ParticipantModelCached = require('../../models/participant/participantCached')
|
|
37
39
|
const ParticipantCurrencyModelCached = require('../../models/participant/participantCurrencyCached')
|
|
38
40
|
const ParticipantLimitCached = require('../../models/participant/participantLimitCached')
|
|
41
|
+
const externalParticipantModelCached = require('../../models/participant/externalParticipantCached')
|
|
39
42
|
const Config = require('../../lib/config')
|
|
40
43
|
const SettlementModelModel = require('../settlement/settlementModel')
|
|
41
|
-
const {
|
|
44
|
+
const { logger } = require('../../shared/logger')
|
|
42
45
|
|
|
43
46
|
const getByNameAndCurrency = async (name, currencyId, ledgerAccountTypeId, isCurrencyActive) => {
|
|
44
47
|
const histTimerParticipantGetByNameAndCurrencyEnd = Metrics.getHistogram(
|
|
@@ -106,6 +109,72 @@ const getByNameAndCurrency = async (name, currencyId, ledgerAccountTypeId, isCur
|
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
const getByIDAndCurrency = async (participantId, currencyId, ledgerAccountTypeId, isCurrencyActive) => {
|
|
113
|
+
const histTimerParticipantGetByIDAndCurrencyEnd = Metrics.getHistogram(
|
|
114
|
+
'model_participant',
|
|
115
|
+
'facade_getByIDAndCurrency - Metrics for participant model',
|
|
116
|
+
['success', 'queryName']
|
|
117
|
+
).startTimer()
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
let participant
|
|
121
|
+
if (Cache.isCacheEnabled()) {
|
|
122
|
+
/* Cached version - fetch data from Models (which we trust are cached) */
|
|
123
|
+
/* find paricipant by ID */
|
|
124
|
+
participant = await ParticipantModelCached.getById(participantId)
|
|
125
|
+
if (participant) {
|
|
126
|
+
/* use the paricipant id and incoming params to prepare the filter */
|
|
127
|
+
const searchFilter = {
|
|
128
|
+
participantId,
|
|
129
|
+
currencyId,
|
|
130
|
+
ledgerAccountTypeId
|
|
131
|
+
}
|
|
132
|
+
if (isCurrencyActive !== undefined) {
|
|
133
|
+
searchFilter.isActive = isCurrencyActive
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* find the participantCurrency by prepared filter */
|
|
137
|
+
const participantCurrency = await ParticipantCurrencyModelCached.findOneByParams(searchFilter)
|
|
138
|
+
|
|
139
|
+
if (participantCurrency) {
|
|
140
|
+
/* mix requested data from participantCurrency */
|
|
141
|
+
participant.participantCurrencyId = participantCurrency.participantCurrencyId
|
|
142
|
+
participant.currencyId = participantCurrency.currencyId
|
|
143
|
+
participant.currencyIsActive = participantCurrency.isActive
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
/* Non-cached version - direct call to DB */
|
|
148
|
+
participant = await Db.from('participant').query(async (builder) => {
|
|
149
|
+
let b = builder
|
|
150
|
+
.where({ 'participant.participantId': participantId })
|
|
151
|
+
.andWhere({ 'pc.currencyId': currencyId })
|
|
152
|
+
.andWhere({ 'pc.ledgerAccountTypeId': ledgerAccountTypeId })
|
|
153
|
+
.innerJoin('participantCurrency AS pc', 'pc.participantId', 'participant.participantId')
|
|
154
|
+
.select(
|
|
155
|
+
'participant.*',
|
|
156
|
+
'pc.participantCurrencyId',
|
|
157
|
+
'pc.currencyId',
|
|
158
|
+
'pc.isActive AS currencyIsActive'
|
|
159
|
+
)
|
|
160
|
+
.first()
|
|
161
|
+
|
|
162
|
+
if (isCurrencyActive !== undefined) {
|
|
163
|
+
b = b.andWhere({ 'pc.isActive': isCurrencyActive })
|
|
164
|
+
}
|
|
165
|
+
return b
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
histTimerParticipantGetByIDAndCurrencyEnd({ success: true, queryName: 'facade_getByIDAndCurrency' })
|
|
170
|
+
|
|
171
|
+
return participant
|
|
172
|
+
} catch (err) {
|
|
173
|
+
histTimerParticipantGetByIDAndCurrencyEnd({ success: false, queryName: 'facade_getByIDAndCurrency' })
|
|
174
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
109
178
|
const getParticipantLimitByParticipantIdAndCurrencyId = async (participantId, currencyId, ledgerAccountTypeId) => {
|
|
110
179
|
try {
|
|
111
180
|
return await Db.from('participant').query(async (builder) => {
|
|
@@ -259,34 +328,30 @@ const addEndpoint = async (participantId, endpoint) => {
|
|
|
259
328
|
try {
|
|
260
329
|
const knex = Db.getKnex()
|
|
261
330
|
return knex.transaction(async trx => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
331
|
+
const endpointType = await knex('endpointType').where({
|
|
332
|
+
name: endpoint.type,
|
|
333
|
+
isActive: 1
|
|
334
|
+
}).select('endpointTypeId').first()
|
|
265
335
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
participantId,
|
|
269
|
-
endpointTypeId: endpointType.endpointTypeId,
|
|
270
|
-
isActive: 1
|
|
271
|
-
})
|
|
272
|
-
if (Array.isArray(existingEndpoint) && existingEndpoint.length > 0) {
|
|
273
|
-
await knex('participantEndpoint').transacting(trx).update({ isActive: 0 }).where('participantEndpointId', existingEndpoint[0].participantEndpointId)
|
|
274
|
-
}
|
|
275
|
-
const newEndpoint = {
|
|
336
|
+
const existingEndpoint = await knex('participantEndpoint').transacting(trx).forUpdate().select('*')
|
|
337
|
+
.where({
|
|
276
338
|
participantId,
|
|
277
339
|
endpointTypeId: endpointType.endpointTypeId,
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
const result = await knex('participantEndpoint').transacting(trx).insert(newEndpoint)
|
|
283
|
-
newEndpoint.participantEndpointId = result[0]
|
|
284
|
-
await trx.commit
|
|
285
|
-
return newEndpoint
|
|
286
|
-
} catch (err) {
|
|
287
|
-
await trx.rollback
|
|
288
|
-
throw err
|
|
340
|
+
isActive: 1
|
|
341
|
+
})
|
|
342
|
+
if (Array.isArray(existingEndpoint) && existingEndpoint.length > 0) {
|
|
343
|
+
await knex('participantEndpoint').transacting(trx).update({ isActive: 0 }).where('participantEndpointId', existingEndpoint[0].participantEndpointId)
|
|
289
344
|
}
|
|
345
|
+
const newEndpoint = {
|
|
346
|
+
participantId,
|
|
347
|
+
endpointTypeId: endpointType.endpointTypeId,
|
|
348
|
+
value: endpoint.value,
|
|
349
|
+
isActive: 1,
|
|
350
|
+
createdBy: 'unknown'
|
|
351
|
+
}
|
|
352
|
+
const result = await knex('participantEndpoint').transacting(trx).insert(newEndpoint)
|
|
353
|
+
newEndpoint.participantEndpointId = result[0]
|
|
354
|
+
return newEndpoint
|
|
290
355
|
})
|
|
291
356
|
} catch (err) {
|
|
292
357
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
@@ -413,73 +478,67 @@ const addLimitAndInitialPosition = async (participantCurrencyId, settlementAccou
|
|
|
413
478
|
try {
|
|
414
479
|
const knex = Db.getKnex()
|
|
415
480
|
return knex.transaction(async trx => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const settlementAccount = await getByNameAndCurrency(limitPositionObj.name, limitPositionObj.currency, settlementModel.settlementAccountTypeId)
|
|
434
|
-
|
|
435
|
-
const participantPosition = {
|
|
436
|
-
participantCurrencyId: positionAccount.participantCurrencyId,
|
|
437
|
-
value: (settlementModel.ledgerAccountTypeId === Enum.Accounts.LedgerAccountType.POSITION ? limitPositionObj.initialPosition : 0),
|
|
438
|
-
reservedValue: 0
|
|
439
|
-
}
|
|
440
|
-
await knex('participantPosition').transacting(trx).insert(participantPosition)
|
|
481
|
+
const limitType = await knex('participantLimitType').where({ name: limitPositionObj.limit.type, isActive: 1 }).select('participantLimitTypeId').first()
|
|
482
|
+
const participantLimit = {
|
|
483
|
+
participantCurrencyId,
|
|
484
|
+
participantLimitTypeId: limitType.participantLimitTypeId,
|
|
485
|
+
value: limitPositionObj.limit.value,
|
|
486
|
+
isActive: 1,
|
|
487
|
+
createdBy: 'unknown'
|
|
488
|
+
}
|
|
489
|
+
const result = await knex('participantLimit').transacting(trx).insert(participantLimit)
|
|
490
|
+
participantLimit.participantLimitId = result[0]
|
|
491
|
+
|
|
492
|
+
const allSettlementModels = await SettlementModelModel.getAll()
|
|
493
|
+
const settlementModels = allSettlementModels.filter(model => model.currencyId === limitPositionObj.currency)
|
|
494
|
+
if (Array.isArray(settlementModels) && settlementModels.length > 0) {
|
|
495
|
+
for (const settlementModel of settlementModels) {
|
|
496
|
+
const positionAccount = await getByNameAndCurrency(limitPositionObj.name, limitPositionObj.currency, settlementModel.ledgerAccountTypeId)
|
|
497
|
+
const settlementAccount = await getByNameAndCurrency(limitPositionObj.name, limitPositionObj.currency, settlementModel.settlementAccountTypeId)
|
|
441
498
|
|
|
442
|
-
const settlementPosition = {
|
|
443
|
-
participantCurrencyId: settlementAccount.participantCurrencyId,
|
|
444
|
-
value: 0,
|
|
445
|
-
reservedValue: 0
|
|
446
|
-
}
|
|
447
|
-
await knex('participantPosition').transacting(trx).insert(settlementPosition)
|
|
448
|
-
if (setCurrencyActive) { // if the flag is true then set the isActive flag for corresponding participantCurrency record to true
|
|
449
|
-
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', positionAccount.participantCurrencyId)
|
|
450
|
-
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', settlementAccount.participantCurrencyId)
|
|
451
|
-
await ParticipantCurrencyModelCached.invalidateParticipantCurrencyCache()
|
|
452
|
-
await ParticipantLimitCached.invalidateParticipantLimitCache()
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
499
|
const participantPosition = {
|
|
457
|
-
participantCurrencyId,
|
|
458
|
-
value: limitPositionObj.initialPosition,
|
|
500
|
+
participantCurrencyId: positionAccount.participantCurrencyId,
|
|
501
|
+
value: (settlementModel.ledgerAccountTypeId === Enum.Accounts.LedgerAccountType.POSITION ? limitPositionObj.initialPosition : 0),
|
|
459
502
|
reservedValue: 0
|
|
460
503
|
}
|
|
461
|
-
|
|
462
|
-
|
|
504
|
+
await knex('participantPosition').transacting(trx).insert(participantPosition)
|
|
505
|
+
|
|
463
506
|
const settlementPosition = {
|
|
464
|
-
participantCurrencyId:
|
|
507
|
+
participantCurrencyId: settlementAccount.participantCurrencyId,
|
|
465
508
|
value: 0,
|
|
466
509
|
reservedValue: 0
|
|
467
510
|
}
|
|
468
511
|
await knex('participantPosition').transacting(trx).insert(settlementPosition)
|
|
469
512
|
if (setCurrencyActive) { // if the flag is true then set the isActive flag for corresponding participantCurrency record to true
|
|
470
|
-
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', participantCurrencyId)
|
|
471
|
-
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId',
|
|
513
|
+
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', positionAccount.participantCurrencyId)
|
|
514
|
+
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', settlementAccount.participantCurrencyId)
|
|
472
515
|
await ParticipantCurrencyModelCached.invalidateParticipantCurrencyCache()
|
|
473
516
|
await ParticipantLimitCached.invalidateParticipantLimitCache()
|
|
474
517
|
}
|
|
475
518
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
519
|
+
} else {
|
|
520
|
+
const participantPosition = {
|
|
521
|
+
participantCurrencyId,
|
|
522
|
+
value: limitPositionObj.initialPosition,
|
|
523
|
+
reservedValue: 0
|
|
524
|
+
}
|
|
525
|
+
const participantPositionResult = await knex('participantPosition').transacting(trx).insert(participantPosition)
|
|
526
|
+
participantPosition.participantPositionId = participantPositionResult[0]
|
|
527
|
+
const settlementPosition = {
|
|
528
|
+
participantCurrencyId: settlementAccountId,
|
|
529
|
+
value: 0,
|
|
530
|
+
reservedValue: 0
|
|
531
|
+
}
|
|
532
|
+
await knex('participantPosition').transacting(trx).insert(settlementPosition)
|
|
533
|
+
if (setCurrencyActive) { // if the flag is true then set the isActive flag for corresponding participantCurrency record to true
|
|
534
|
+
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', participantCurrencyId)
|
|
535
|
+
await knex('participantCurrency').transacting(trx).update({ isActive: 1 }).where('participantCurrencyId', settlementAccountId)
|
|
536
|
+
await ParticipantCurrencyModelCached.invalidateParticipantCurrencyCache()
|
|
537
|
+
await ParticipantLimitCached.invalidateParticipantLimitCache()
|
|
538
|
+
}
|
|
482
539
|
}
|
|
540
|
+
|
|
541
|
+
return true
|
|
483
542
|
})
|
|
484
543
|
} catch (err) {
|
|
485
544
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
@@ -510,7 +569,7 @@ const addLimitAndInitialPosition = async (participantCurrencyId, settlementAccou
|
|
|
510
569
|
|
|
511
570
|
const adjustLimits = async (participantCurrencyId, limit, trx) => {
|
|
512
571
|
try {
|
|
513
|
-
const trxFunction = async (trx
|
|
572
|
+
const trxFunction = async (trx) => {
|
|
514
573
|
try {
|
|
515
574
|
const limitType = await knex('participantLimitType').where({ name: limit.type, isActive: 1 }).select('participantLimitTypeId').first()
|
|
516
575
|
// const limitType = await trx.first('participantLimitTypeId').from('participantLimitType').where({ 'name': limit.type, 'isActive': 1 })
|
|
@@ -535,23 +594,17 @@ const adjustLimits = async (participantCurrencyId, limit, trx) => {
|
|
|
535
594
|
}
|
|
536
595
|
const result = await knex('participantLimit').transacting(trx).insert(newLimit)
|
|
537
596
|
newLimit.participantLimitId = result[0]
|
|
538
|
-
if (doCommit) {
|
|
539
|
-
await trx.commit
|
|
540
|
-
}
|
|
541
597
|
return {
|
|
542
598
|
participantLimit: newLimit
|
|
543
599
|
}
|
|
544
600
|
} catch (err) {
|
|
545
|
-
if (doCommit) {
|
|
546
|
-
await trx.rollback
|
|
547
|
-
}
|
|
548
601
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
549
602
|
}
|
|
550
603
|
}
|
|
551
604
|
|
|
552
605
|
const knex = Db.getKnex()
|
|
553
606
|
if (trx) {
|
|
554
|
-
return trxFunction(trx
|
|
607
|
+
return trxFunction(trx)
|
|
555
608
|
} else {
|
|
556
609
|
return knex.transaction(trxFunction)
|
|
557
610
|
}
|
|
@@ -640,34 +693,28 @@ const addHubAccountAndInitPosition = async (participantId, currencyId, ledgerAcc
|
|
|
640
693
|
try {
|
|
641
694
|
const knex = Db.getKnex()
|
|
642
695
|
return knex.transaction(async trx => {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
participantCurrency,
|
|
666
|
-
participantPosition
|
|
667
|
-
}
|
|
668
|
-
} catch (err) {
|
|
669
|
-
await trx.rollback
|
|
670
|
-
throw err
|
|
696
|
+
let result
|
|
697
|
+
const participantCurrency = {
|
|
698
|
+
participantId,
|
|
699
|
+
currencyId,
|
|
700
|
+
ledgerAccountTypeId,
|
|
701
|
+
createdBy: 'unknown',
|
|
702
|
+
isActive: 1,
|
|
703
|
+
createdDate: Time.getUTCString(new Date())
|
|
704
|
+
}
|
|
705
|
+
result = await knex('participantCurrency').transacting(trx).insert(participantCurrency)
|
|
706
|
+
await ParticipantCurrencyModelCached.invalidateParticipantCurrencyCache()
|
|
707
|
+
participantCurrency.participantCurrencyId = result[0]
|
|
708
|
+
const participantPosition = {
|
|
709
|
+
participantCurrencyId: participantCurrency.participantCurrencyId,
|
|
710
|
+
value: 0,
|
|
711
|
+
reservedValue: 0
|
|
712
|
+
}
|
|
713
|
+
result = await knex('participantPosition').transacting(trx).insert(participantPosition)
|
|
714
|
+
participantPosition.participantPositionId = result[0]
|
|
715
|
+
return {
|
|
716
|
+
participantCurrency,
|
|
717
|
+
participantPosition
|
|
671
718
|
}
|
|
672
719
|
})
|
|
673
720
|
} catch (err) {
|
|
@@ -706,7 +753,7 @@ const getAllNonHubParticipantsWithCurrencies = async (trx) => {
|
|
|
706
753
|
try {
|
|
707
754
|
const HUB_ACCOUNT_NAME = Config.HUB_NAME
|
|
708
755
|
const knex = Db.getKnex()
|
|
709
|
-
const trxFunction = async (trx
|
|
756
|
+
const trxFunction = async (trx) => {
|
|
710
757
|
try {
|
|
711
758
|
const res = await knex.distinct('participant.participantId', 'pc.participantId', 'pc.currencyId')
|
|
712
759
|
.from('participant')
|
|
@@ -714,19 +761,13 @@ const getAllNonHubParticipantsWithCurrencies = async (trx) => {
|
|
|
714
761
|
.whereNot('participant.name', HUB_ACCOUNT_NAME)
|
|
715
762
|
.transacting(trx)
|
|
716
763
|
|
|
717
|
-
if (doCommit) {
|
|
718
|
-
await trx.commit
|
|
719
|
-
}
|
|
720
764
|
return res
|
|
721
765
|
} catch (err) {
|
|
722
|
-
if (doCommit) {
|
|
723
|
-
await trx.rollback
|
|
724
|
-
}
|
|
725
766
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
726
767
|
}
|
|
727
768
|
}
|
|
728
769
|
if (trx) {
|
|
729
|
-
return trxFunction(trx
|
|
770
|
+
return trxFunction(trx)
|
|
730
771
|
} else {
|
|
731
772
|
return knex.transaction(trxFunction)
|
|
732
773
|
}
|
|
@@ -735,9 +776,36 @@ const getAllNonHubParticipantsWithCurrencies = async (trx) => {
|
|
|
735
776
|
}
|
|
736
777
|
}
|
|
737
778
|
|
|
779
|
+
const getExternalParticipantIdByNameOrCreate = async ({ name, proxyId }) => {
|
|
780
|
+
try {
|
|
781
|
+
let externalFsp = await externalParticipantModelCached.getByName(name)
|
|
782
|
+
if (!externalFsp) {
|
|
783
|
+
const proxy = await ParticipantModelCached.getByName(proxyId)
|
|
784
|
+
if (!proxy) {
|
|
785
|
+
throw new Error(`Proxy participant not found: ${proxyId}`)
|
|
786
|
+
}
|
|
787
|
+
const externalParticipantId = await externalParticipantModelCached.create({
|
|
788
|
+
name,
|
|
789
|
+
proxyId: proxy.participantId
|
|
790
|
+
})
|
|
791
|
+
externalFsp = externalParticipantId
|
|
792
|
+
? { externalParticipantId }
|
|
793
|
+
: await externalParticipantModelCached.getByName(name)
|
|
794
|
+
}
|
|
795
|
+
const id = externalFsp?.externalParticipantId
|
|
796
|
+
logger.verbose('getExternalParticipantIdByNameOrCreate result:', { id, name })
|
|
797
|
+
return id
|
|
798
|
+
} catch (err) {
|
|
799
|
+
logger.child({ name, proxyId }).warn('error in getExternalParticipantIdByNameOrCreate:', err)
|
|
800
|
+
return null
|
|
801
|
+
// todo: think, if we need to rethrow an error here?
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
738
805
|
module.exports = {
|
|
739
806
|
addHubAccountAndInitPosition,
|
|
740
807
|
getByNameAndCurrency,
|
|
808
|
+
getByIDAndCurrency,
|
|
741
809
|
getParticipantLimitByParticipantIdAndCurrencyId,
|
|
742
810
|
getEndpoint,
|
|
743
811
|
getAllEndpoints,
|
|
@@ -750,5 +818,6 @@ module.exports = {
|
|
|
750
818
|
getParticipantLimitsByParticipantId,
|
|
751
819
|
getAllAccountsByNameAndCurrency,
|
|
752
820
|
getLimitsForAllParticipants,
|
|
753
|
-
getAllNonHubParticipantsWithCurrencies
|
|
821
|
+
getAllNonHubParticipantsWithCurrencies,
|
|
822
|
+
getExternalParticipantIdByNameOrCreate
|
|
754
823
|
}
|
|
@@ -43,7 +43,8 @@ exports.create = async (participant) => {
|
|
|
43
43
|
try {
|
|
44
44
|
const result = await Db.from('participant').insert({
|
|
45
45
|
name: participant.name,
|
|
46
|
-
createdBy: 'unknown'
|
|
46
|
+
createdBy: 'unknown',
|
|
47
|
+
isProxy: !!participant.isProxy
|
|
47
48
|
})
|
|
48
49
|
return result
|
|
49
50
|
} catch (err) {
|
|
@@ -43,7 +43,7 @@ exports.create = async (participantId, currencyId, ledgerAccountTypeId, isActive
|
|
|
43
43
|
|
|
44
44
|
exports.getAll = async () => {
|
|
45
45
|
try {
|
|
46
|
-
return Db.from('participantCurrency').find({}, { order: 'participantCurrencyId asc' })
|
|
46
|
+
return await Db.from('participantCurrency').find({}, { order: 'participantCurrencyId asc' })
|
|
47
47
|
} catch (err) {
|
|
48
48
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
49
49
|
}
|
|
@@ -107,23 +107,17 @@ const destroyByParticipantId = async (participantId) => {
|
|
|
107
107
|
const createParticipantPositionRecords = async (participantPositions, trx) => {
|
|
108
108
|
try {
|
|
109
109
|
const knex = Db.getKnex()
|
|
110
|
-
const trxFunction = async (trx
|
|
110
|
+
const trxFunction = async (trx) => {
|
|
111
111
|
try {
|
|
112
112
|
await knex
|
|
113
113
|
.batchInsert('participantPosition', participantPositions)
|
|
114
114
|
.transacting(trx)
|
|
115
|
-
if (doCommit) {
|
|
116
|
-
await trx.commit
|
|
117
|
-
}
|
|
118
115
|
} catch (err) {
|
|
119
|
-
if (doCommit) {
|
|
120
|
-
await trx.rollback
|
|
121
|
-
}
|
|
122
116
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
123
117
|
}
|
|
124
118
|
}
|
|
125
119
|
if (trx) {
|
|
126
|
-
return trxFunction(trx
|
|
120
|
+
return trxFunction(trx)
|
|
127
121
|
} else {
|
|
128
122
|
return knex.transaction(trxFunction)
|
|
129
123
|
}
|
|
@@ -63,6 +63,28 @@ const getLatestTransferStateChangesByTransferIdList = async (trx, transfersIdLis
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
const getLatestFxTransferStateChangesByCommitRequestIdList = async (trx, commitRequestIdList) => {
|
|
67
|
+
const knex = await Db.getKnex()
|
|
68
|
+
try {
|
|
69
|
+
const latestFxTransferStateChanges = {}
|
|
70
|
+
const results = await knex('fxTransferStateChange')
|
|
71
|
+
.transacting(trx)
|
|
72
|
+
.whereIn('fxTransferStateChange.commitRequestId', commitRequestIdList)
|
|
73
|
+
.orderBy('fxTransferStateChangeId', 'desc')
|
|
74
|
+
.select('*')
|
|
75
|
+
|
|
76
|
+
for (const result of results) {
|
|
77
|
+
if (!latestFxTransferStateChanges[result.commitRequestId]) {
|
|
78
|
+
latestFxTransferStateChanges[result.commitRequestId] = result
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return latestFxTransferStateChanges
|
|
82
|
+
} catch (err) {
|
|
83
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
84
|
+
throw err
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
66
88
|
const getAllParticipantCurrency = async (trx) => {
|
|
67
89
|
const knex = await Db.getKnex()
|
|
68
90
|
if (trx) {
|
|
@@ -138,6 +160,11 @@ const bulkInsertTransferStateChanges = async (trx, transferStateChangeList) => {
|
|
|
138
160
|
return await knex.batchInsert('transferStateChange', transferStateChangeList).transacting(trx)
|
|
139
161
|
}
|
|
140
162
|
|
|
163
|
+
const bulkInsertFxTransferStateChanges = async (trx, fxTransferStateChangeList) => {
|
|
164
|
+
const knex = await Db.getKnex()
|
|
165
|
+
return await knex.batchInsert('fxTransferStateChange', fxTransferStateChangeList).transacting(trx)
|
|
166
|
+
}
|
|
167
|
+
|
|
141
168
|
const bulkInsertParticipantPositionChanges = async (trx, participantPositionChangeList) => {
|
|
142
169
|
const knex = await Db.getKnex()
|
|
143
170
|
return await knex.batchInsert('participantPositionChange', participantPositionChangeList).transacting(trx)
|
|
@@ -184,14 +211,76 @@ const getTransferByIdsForReserve = async (trx, transferIds) => {
|
|
|
184
211
|
return {}
|
|
185
212
|
}
|
|
186
213
|
|
|
214
|
+
const getFxTransferInfoList = async (trx, commitRequestId, transferParticipantRoleTypeId, ledgerEntryTypeId) => {
|
|
215
|
+
try {
|
|
216
|
+
const knex = await Db.getKnex()
|
|
217
|
+
const transferInfos = await knex('fxTransferParticipant')
|
|
218
|
+
.transacting(trx)
|
|
219
|
+
.where({
|
|
220
|
+
'fxTransferParticipant.transferParticipantRoleTypeId': transferParticipantRoleTypeId,
|
|
221
|
+
'fxTransferParticipant.ledgerEntryTypeId': ledgerEntryTypeId
|
|
222
|
+
})
|
|
223
|
+
.whereIn('fxTransferParticipant.commitRequestId', commitRequestId)
|
|
224
|
+
.select(
|
|
225
|
+
'fxTransferParticipant.*'
|
|
226
|
+
)
|
|
227
|
+
const info = {}
|
|
228
|
+
// This should key the transfer info with the latest transferStateChangeId
|
|
229
|
+
for (const transferInfo of transferInfos) {
|
|
230
|
+
if (!(transferInfo.commitRequestId in info)) {
|
|
231
|
+
info[transferInfo.commitRequestId] = transferInfo
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return info
|
|
235
|
+
} catch (err) {
|
|
236
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
237
|
+
throw err
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// This model assumes that there is only one RESERVED participantPositionChange per commitRequestId and participantPositionId.
|
|
242
|
+
// If an fxTransfer use case changes in the future where more than one reservation happens to a participant's account
|
|
243
|
+
// for the same commitRequestId, this model will need to be updated.
|
|
244
|
+
const getReservedPositionChangesByCommitRequestIds = async (trx, commitRequestIdList) => {
|
|
245
|
+
try {
|
|
246
|
+
const knex = await Db.getKnex()
|
|
247
|
+
const participantPositionChanges = await knex('fxTransferStateChange')
|
|
248
|
+
.transacting(trx)
|
|
249
|
+
.whereIn('fxTransferStateChange.commitRequestId', commitRequestIdList)
|
|
250
|
+
.where('fxTransferStateChange.transferStateId', Enum.Transfers.TransferInternalState.RESERVED)
|
|
251
|
+
.leftJoin('participantPositionChange AS ppc', 'ppc.fxTransferStateChangeId', 'fxTransferStateChange.fxTransferStateChangeId')
|
|
252
|
+
.select(
|
|
253
|
+
'ppc.*',
|
|
254
|
+
'fxTransferStateChange.commitRequestId AS commitRequestId'
|
|
255
|
+
)
|
|
256
|
+
const info = {}
|
|
257
|
+
for (const participantPositionChange of participantPositionChanges) {
|
|
258
|
+
if (!(participantPositionChange.commitRequestId in info)) {
|
|
259
|
+
info[participantPositionChange.commitRequestId] = {}
|
|
260
|
+
}
|
|
261
|
+
if (participantPositionChange.participantCurrencyId) {
|
|
262
|
+
info[participantPositionChange.commitRequestId][participantPositionChange.participantCurrencyId] = participantPositionChange
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return info
|
|
266
|
+
} catch (err) {
|
|
267
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
268
|
+
throw err
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
187
272
|
module.exports = {
|
|
188
273
|
startDbTransaction,
|
|
189
274
|
getLatestTransferStateChangesByTransferIdList,
|
|
275
|
+
getLatestFxTransferStateChangesByCommitRequestIdList,
|
|
190
276
|
getPositionsByAccountIdsForUpdate,
|
|
191
277
|
updateParticipantPosition,
|
|
192
278
|
bulkInsertTransferStateChanges,
|
|
279
|
+
bulkInsertFxTransferStateChanges,
|
|
193
280
|
bulkInsertParticipantPositionChanges,
|
|
194
281
|
getAllParticipantCurrency,
|
|
195
282
|
getTransferInfoList,
|
|
196
|
-
getTransferByIdsForReserve
|
|
283
|
+
getTransferByIdsForReserve,
|
|
284
|
+
getFxTransferInfoList,
|
|
285
|
+
getReservedPositionChangesByCommitRequestIds
|
|
197
286
|
}
|
|
@@ -229,11 +229,13 @@ const prepareChangeParticipantPositionTransaction = async (transferList) => {
|
|
|
229
229
|
const processedTransfersKeysList = Object.keys(processedTransfers)
|
|
230
230
|
const batchParticipantPositionChange = []
|
|
231
231
|
for (const keyIndex in processedTransfersKeysList) {
|
|
232
|
-
const { runningPosition, runningReservedValue } = processedTransfers[processedTransfersKeysList[keyIndex]]
|
|
232
|
+
const { transferAmount, runningPosition, runningReservedValue } = processedTransfers[processedTransfersKeysList[keyIndex]]
|
|
233
233
|
const participantPositionChange = {
|
|
234
234
|
participantPositionId: initialParticipantPosition.participantPositionId,
|
|
235
|
+
participantCurrencyId: participantCurrency.participantCurrencyId,
|
|
235
236
|
transferStateChangeId: processedTransferStateChangeIdList[keyIndex],
|
|
236
237
|
value: runningPosition,
|
|
238
|
+
change: transferAmount.toNumber(),
|
|
237
239
|
// processBatch: <uuid> - a single value uuid for this entire batch to make sure the set of transfers in this batch can be clearly grouped
|
|
238
240
|
reservedValue: runningReservedValue
|
|
239
241
|
}
|
|
@@ -241,11 +243,9 @@ const prepareChangeParticipantPositionTransaction = async (transferList) => {
|
|
|
241
243
|
}
|
|
242
244
|
batchParticipantPositionChange.length && await knex.batchInsert('participantPositionChange', batchParticipantPositionChange).transacting(trx)
|
|
243
245
|
histTimerPersistTransferStateChangeEnd({ success: true, queryName: 'facade_prepareChangeParticipantPositionTransaction_transaction_PersistTransferState' })
|
|
244
|
-
await trx.commit()
|
|
245
246
|
histTimerChangeParticipantPositionTransEnd({ success: true, queryName: 'facade_prepareChangeParticipantPositionTransaction_transaction' })
|
|
246
247
|
} catch (err) {
|
|
247
248
|
Logger.isErrorEnabled && Logger.error(err)
|
|
248
|
-
await trx.rollback()
|
|
249
249
|
histTimerChangeParticipantPositionTransEnd({ success: false, queryName: 'facade_prepareChangeParticipantPositionTransaction_transaction' })
|
|
250
250
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
251
251
|
}
|
|
@@ -292,16 +292,16 @@ const changeParticipantPositionTransaction = async (participantCurrencyId, isRev
|
|
|
292
292
|
const insertedTransferStateChange = await knex('transferStateChange').transacting(trx).where({ transferId: transferStateChange.transferId }).forUpdate().first().orderBy('transferStateChangeId', 'desc')
|
|
293
293
|
const participantPositionChange = {
|
|
294
294
|
participantPositionId: participantPosition.participantPositionId,
|
|
295
|
+
participantCurrencyId,
|
|
295
296
|
transferStateChangeId: insertedTransferStateChange.transferStateChangeId,
|
|
296
297
|
value: latestPosition,
|
|
298
|
+
change: isReversal ? -amount : amount,
|
|
297
299
|
reservedValue: participantPosition.reservedValue,
|
|
298
300
|
createdDate: transactionTimestamp
|
|
299
301
|
}
|
|
300
302
|
await knex('participantPositionChange').transacting(trx).insert(participantPositionChange)
|
|
301
|
-
await trx.commit()
|
|
302
303
|
histTimerChangeParticipantPositionTransactionEnd({ success: true, queryName: 'facade_changeParticipantPositionTransaction' })
|
|
303
304
|
} catch (err) {
|
|
304
|
-
await trx.rollback()
|
|
305
305
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
306
306
|
}
|
|
307
307
|
}).catch((err) => {
|