@mojaloop/central-ledger 17.4.1 → 17.5.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/CHANGELOG.md +7 -0
- package/README.md +11 -4
- package/config/default.json +4 -1
- package/package.json +2 -2
- package/scripts/_wait4_all.js +1 -1
- package/src/domain/position/binProcessor.js +38 -7
- package/src/domain/position/fulfil.js +153 -0
- package/src/handlers/transfers/handler.js +18 -1
- package/src/models/position/batch.js +29 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [17.5.0](https://github.com/mojaloop/central-ledger/compare/v17.4.1...v17.5.0) (2023-12-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **mojaloop/#3524:** add position fulfil to binprocessor ([#990](https://github.com/mojaloop/central-ledger/issues/990)) ([dddedf6](https://github.com/mojaloop/central-ledger/commit/dddedf6adc03b441dcdc177f51d3850b79cfb144)), closes [mojaloop/#3524](https://github.com/mojaloop/project/issues/3524)
|
|
11
|
+
|
|
5
12
|
### [17.4.1](https://github.com/mojaloop/central-ledger/compare/v17.4.0...v17.4.1) (2023-12-05)
|
|
6
13
|
|
|
7
14
|
|
package/README.md
CHANGED
|
@@ -96,13 +96,14 @@ diverges from the defaults.
|
|
|
96
96
|
You can configure the customized topic names in the config. Each position action key
|
|
97
97
|
refers to position messages with associated actions.
|
|
98
98
|
|
|
99
|
-
NOTE: Only POSITION.PREPARE is supported at this time, with additional event-type-actions being added later when required.
|
|
99
|
+
NOTE: Only POSITION.PREPARE and POSITION.COMMIT is supported at this time, with additional event-type-actions being added later when required.
|
|
100
100
|
|
|
101
101
|
```
|
|
102
102
|
"KAFKA": {
|
|
103
103
|
"EVENT_TYPE_ACTION_TOPIC_MAP" : {
|
|
104
104
|
"POSITION":{
|
|
105
|
-
"PREPARE": "topic-transfer-position-batch"
|
|
105
|
+
"PREPARE": "topic-transfer-position-batch",
|
|
106
|
+
"COMMIT": "topic-transfer-position-batch"
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
}
|
|
@@ -123,7 +124,10 @@ Batch processing can be enabled in the transfer execution flow. Follow the steps
|
|
|
123
124
|
"EVENT_TYPE_ACTION_TOPIC_MAP" : {
|
|
124
125
|
"POSITION":{
|
|
125
126
|
"PREPARE": "topic-transfer-position-batch",
|
|
126
|
-
"BULK_PREPARE": "topic-transfer-position"
|
|
127
|
+
"BULK_PREPARE": "topic-transfer-position",
|
|
128
|
+
"COMMIT": "topic-transfer-position-batch",
|
|
129
|
+
"BULK_COMMIT": "topic-transfer-position",
|
|
130
|
+
"RESERVE": "topic-transfer-position",
|
|
127
131
|
}
|
|
128
132
|
}
|
|
129
133
|
}
|
|
@@ -243,7 +247,10 @@ env "CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__PREPARE=topic-transfer-
|
|
|
243
247
|
```
|
|
244
248
|
- Additionally, run position batch handler in a new terminal
|
|
245
249
|
```
|
|
246
|
-
|
|
250
|
+
export CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__PREPARE=topic-transfer-position-batch
|
|
251
|
+
export CLEDG_KAFKA__EVENT_TYPE_ACTION_TOPIC_MAP__POSITION__COMMIT=topic-transfer-position-batch
|
|
252
|
+
export CLEDG_HANDLERS__API__DISABLED=true
|
|
253
|
+
node src/handlers/index.js handler --positionbatch
|
|
247
254
|
```
|
|
248
255
|
- Run tests using `npx tape 'test/integration-override/**/handlerBatch.test.js'`
|
|
249
256
|
|
package/config/default.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-ledger",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.5.0",
|
|
4
4
|
"description": "Central ledger hosted by a scheme to record and settle transfers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"hapi-auth-bearer-token": "8.0.0",
|
|
112
112
|
"hapi-swagger": "17.2.0",
|
|
113
113
|
"ilp-packet": "2.2.0",
|
|
114
|
-
"knex": "3.0
|
|
114
|
+
"knex": "3.1.0",
|
|
115
115
|
"lodash": "4.17.21",
|
|
116
116
|
"moment": "2.29.4",
|
|
117
117
|
"mongo-uri-builder": "^4.0.0",
|
package/scripts/_wait4_all.js
CHANGED
|
@@ -34,6 +34,7 @@ const Logger = require('@mojaloop/central-services-logger')
|
|
|
34
34
|
const BatchPositionModel = require('../../models/position/batch')
|
|
35
35
|
const BatchPositionModelCached = require('../../models/position/batchCached')
|
|
36
36
|
const PositionPrepareDomain = require('./prepare')
|
|
37
|
+
const PositionFulfilDomain = require('./fulfil')
|
|
37
38
|
const SettlementModelCached = require('../../models/settlement/settlementModelCached')
|
|
38
39
|
const Enum = require('@mojaloop/central-services-shared').Enum
|
|
39
40
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
@@ -52,9 +53,12 @@ const participantFacade = require('../../models/participant/facade')
|
|
|
52
53
|
*/
|
|
53
54
|
const processBins = async (bins, trx) => {
|
|
54
55
|
const transferIdList = []
|
|
55
|
-
iterateThroughBins(bins, (_accountID, _action, item) => {
|
|
56
|
+
await iterateThroughBins(bins, (_accountID, _action, item) => {
|
|
56
57
|
if (item.decodedPayload?.transferId) {
|
|
57
58
|
transferIdList.push(item.decodedPayload.transferId)
|
|
59
|
+
// get transferId from uriParams for fulfil messages
|
|
60
|
+
} else if (item.message?.value?.content?.uriParams?.id) {
|
|
61
|
+
transferIdList.push(item.message.value.content.uriParams.id)
|
|
58
62
|
}
|
|
59
63
|
})
|
|
60
64
|
// Pre fetch latest transferStates for all the transferIds in the account-bin
|
|
@@ -120,6 +124,13 @@ const processBins = async (bins, trx) => {
|
|
|
120
124
|
...settlementCurrencyIds.map(pc => pc.participantCurrencyId)
|
|
121
125
|
])
|
|
122
126
|
|
|
127
|
+
const latestTransferInfoByTransferId = await BatchPositionModel.getTransferInfoList(
|
|
128
|
+
trx,
|
|
129
|
+
transferIdList,
|
|
130
|
+
Enum.Accounts.TransferParticipantRoleType.PAYEE_DFSP,
|
|
131
|
+
Enum.Accounts.LedgerEntryType.PRINCIPLE_VALUE
|
|
132
|
+
)
|
|
133
|
+
|
|
123
134
|
let notifyMessages = []
|
|
124
135
|
let limitAlarms = []
|
|
125
136
|
|
|
@@ -127,6 +138,14 @@ const processBins = async (bins, trx) => {
|
|
|
127
138
|
for (const accountID in bins) {
|
|
128
139
|
const accountBin = bins[accountID]
|
|
129
140
|
const actions = Object.keys(accountBin)
|
|
141
|
+
const isSubset = (array1, array2) =>
|
|
142
|
+
array2.every((element) => array1.includes(element))
|
|
143
|
+
// If non-prepare/non-commit action found, log error
|
|
144
|
+
// We need to remove this once we implement all the actions
|
|
145
|
+
if (!isSubset(['prepare', 'commit'], actions)) {
|
|
146
|
+
Logger.isErrorEnabled && Logger.error('Only prepare/commit actions are allowed in a batch')
|
|
147
|
+
// throw new Error('Only prepare action is allowed in a batch')
|
|
148
|
+
}
|
|
130
149
|
|
|
131
150
|
const settlementParticipantPosition = positions[accountIdMap[accountID].settlementCurrencyId].value
|
|
132
151
|
const settlementModel = currencyIdMap[accountIdMap[accountID].currencyId].settlementModel
|
|
@@ -146,12 +165,24 @@ const processBins = async (bins, trx) => {
|
|
|
146
165
|
let accumulatedTransferStateChanges = []
|
|
147
166
|
let accumulatedPositionChanges = []
|
|
148
167
|
|
|
149
|
-
// If
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
// If fulfil action found then call processPositionPrepareBin function
|
|
169
|
+
const fulfilActionResult = await PositionFulfilDomain.processPositionFulfilBin(
|
|
170
|
+
accountBin.commit,
|
|
171
|
+
accumulatedPositionValue,
|
|
172
|
+
accumulatedPositionReservedValue,
|
|
173
|
+
accumulatedTransferStates,
|
|
174
|
+
latestTransferInfoByTransferId
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// Update accumulated values
|
|
178
|
+
accumulatedPositionValue = fulfilActionResult.accumulatedPositionValue
|
|
179
|
+
accumulatedPositionReservedValue = fulfilActionResult.accumulatedPositionReservedValue
|
|
180
|
+
accumulatedTransferStates = fulfilActionResult.accumulatedTransferStates
|
|
181
|
+
// Append accumulated arrays
|
|
182
|
+
accumulatedTransferStateChanges = accumulatedTransferStateChanges.concat(fulfilActionResult.accumulatedTransferStateChanges)
|
|
183
|
+
accumulatedPositionChanges = accumulatedPositionChanges.concat(fulfilActionResult.accumulatedPositionChanges)
|
|
184
|
+
notifyMessages = notifyMessages.concat(fulfilActionResult.notifyMessages)
|
|
185
|
+
|
|
155
186
|
// If prepare action found then call processPositionPrepareBin function
|
|
156
187
|
const prepareActionResult = await PositionPrepareDomain.processPositionPrepareBin(
|
|
157
188
|
accountBin.prepare,
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const { Enum } = require('@mojaloop/central-services-shared')
|
|
2
|
+
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
3
|
+
const Config = require('../../lib/config')
|
|
4
|
+
const Utility = require('@mojaloop/central-services-shared').Util
|
|
5
|
+
const MLNumber = require('@mojaloop/ml-number')
|
|
6
|
+
const Logger = require('@mojaloop/central-services-logger')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @function processPositionFulfilBin
|
|
10
|
+
*
|
|
11
|
+
* @async
|
|
12
|
+
* @description This is the domain function to process a bin of position-fulfil messages of a single participant account.
|
|
13
|
+
*
|
|
14
|
+
* @param {array} binItems - an array of objects that contain a position fulfil message and its span. {message, span}
|
|
15
|
+
* @param {number} accumulatedPositionValue - value of position accumulated so far from previous bin processing
|
|
16
|
+
* @param {number} accumulatedPositionReservedValue - value of position reserved accumulated so far, not used but kept for consistency
|
|
17
|
+
* @param {object} accumulatedTransferStates - object with transfer id keys and transfer state id values. Used to check if transfer is in correct state for processing. Clone and update states for output.
|
|
18
|
+
* @param {object} transferInfoList - object with transfer id keys and transfer info values. Used to pass transfer info to domain function.
|
|
19
|
+
* @returns {object} - Returns an object containing accumulatedPositionValue, accumulatedPositionReservedValue, accumulatedTransferStateChanges, accumulatedTransferStates, resultMessages, limitAlarms or throws an error if failed
|
|
20
|
+
*/
|
|
21
|
+
const processPositionFulfilBin = async (
|
|
22
|
+
binItems,
|
|
23
|
+
accumulatedPositionValue,
|
|
24
|
+
accumulatedPositionReservedValue,
|
|
25
|
+
accumulatedTransferStates,
|
|
26
|
+
transferInfoList
|
|
27
|
+
) => {
|
|
28
|
+
const transferStateChanges = []
|
|
29
|
+
const participantPositionChanges = []
|
|
30
|
+
const resultMessages = []
|
|
31
|
+
const accumulatedTransferStatesCopy = Object.assign({}, accumulatedTransferStates)
|
|
32
|
+
let runningPosition = new MLNumber(accumulatedPositionValue)
|
|
33
|
+
|
|
34
|
+
if (binItems && binItems.length > 0) {
|
|
35
|
+
for (const binItem of binItems) {
|
|
36
|
+
let transferStateId
|
|
37
|
+
let reason
|
|
38
|
+
let resultMessage
|
|
39
|
+
const transferId = binItem.message.value.content.uriParams.id
|
|
40
|
+
const payeeFsp = binItem.message.value.from
|
|
41
|
+
const payerFsp = binItem.message.value.to
|
|
42
|
+
const transfer = binItem.decodedPayload
|
|
43
|
+
Logger.isDebugEnabled && Logger.debug(`processPositionFulfilBin::transfer:processingMessage: ${JSON.stringify(transfer)}`)
|
|
44
|
+
Logger.isDebugEnabled && Logger.debug(`accumulatedTransferStates: ${JSON.stringify(accumulatedTransferStates)}`)
|
|
45
|
+
// Inform payee dfsp if transfer is not in RECEIVED_FULFIL state, skip making any transfer state changes
|
|
46
|
+
if (accumulatedTransferStates[transferId] !== Enum.Transfers.TransferInternalState.RECEIVED_FULFIL) {
|
|
47
|
+
// forward same headers from the prepare message, except the content-length header
|
|
48
|
+
// set destination to payeefsp and source to switch
|
|
49
|
+
const headers = { ...binItem.message.value.content.headers }
|
|
50
|
+
headers[Enum.Http.Headers.FSPIOP.DESTINATION] = payeeFsp
|
|
51
|
+
headers[Enum.Http.Headers.FSPIOP.SOURCE] = Enum.Http.Headers.FSPIOP.SWITCH.value
|
|
52
|
+
delete headers['content-length']
|
|
53
|
+
|
|
54
|
+
const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(
|
|
55
|
+
`Invalid State: ${accumulatedTransferStates[transferId]} - expected: ${Enum.Transfers.TransferInternalState.RECEIVED_FULFIL}`
|
|
56
|
+
).toApiErrorObject(Config.ERROR_HANDLING)
|
|
57
|
+
const state = Utility.StreamingProtocol.createEventState(
|
|
58
|
+
Enum.Events.EventStatus.FAILURE.status,
|
|
59
|
+
fspiopError.errorInformation.errorCode,
|
|
60
|
+
fspiopError.errorInformation.errorDescription
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent(
|
|
64
|
+
transferId,
|
|
65
|
+
Enum.Kafka.Topics.NOTIFICATION,
|
|
66
|
+
Enum.Events.Event.Action.FULFIL,
|
|
67
|
+
state
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
resultMessage = Utility.StreamingProtocol.createMessage(
|
|
71
|
+
transferId,
|
|
72
|
+
payeeFsp,
|
|
73
|
+
Enum.Http.Headers.FSPIOP.SWITCH.value,
|
|
74
|
+
metadata,
|
|
75
|
+
headers,
|
|
76
|
+
fspiopError,
|
|
77
|
+
{ id: transferId },
|
|
78
|
+
'application/json'
|
|
79
|
+
)
|
|
80
|
+
} else {
|
|
81
|
+
const transferInfo = transferInfoList[transferId]
|
|
82
|
+
|
|
83
|
+
// forward same headers from the prepare message, except the content-length header
|
|
84
|
+
const headers = { ...binItem.message.value.content.headers }
|
|
85
|
+
delete headers['content-length']
|
|
86
|
+
|
|
87
|
+
const state = Utility.StreamingProtocol.createEventState(
|
|
88
|
+
Enum.Events.EventStatus.SUCCESS.status,
|
|
89
|
+
null,
|
|
90
|
+
null
|
|
91
|
+
)
|
|
92
|
+
const metadata = Utility.StreamingProtocol.createMetadataWithCorrelatedEvent(
|
|
93
|
+
transferId,
|
|
94
|
+
Enum.Kafka.Topics.TRANSFER,
|
|
95
|
+
Enum.Events.Event.Action.COMMIT,
|
|
96
|
+
state
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
resultMessage = Utility.StreamingProtocol.createMessage(
|
|
100
|
+
transferId,
|
|
101
|
+
payerFsp,
|
|
102
|
+
payeeFsp,
|
|
103
|
+
metadata,
|
|
104
|
+
headers,
|
|
105
|
+
transfer,
|
|
106
|
+
{ id: transferId },
|
|
107
|
+
'application/json'
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
transferStateId = Enum.Transfers.TransferState.COMMITTED
|
|
111
|
+
// Amounts in `transferParticipant` for the payee are stored as negative values
|
|
112
|
+
runningPosition = new MLNumber(runningPosition.add(transferInfo.amount).toFixed(Config.AMOUNT.SCALE))
|
|
113
|
+
|
|
114
|
+
const participantPositionChange = {
|
|
115
|
+
transferId, // Need to delete this in bin processor while updating transferStateChangeId
|
|
116
|
+
transferStateChangeId: null, // Need to update this in bin processor while executing queries
|
|
117
|
+
value: runningPosition.toNumber(),
|
|
118
|
+
reservedValue: accumulatedPositionReservedValue
|
|
119
|
+
}
|
|
120
|
+
participantPositionChanges.push(participantPositionChange)
|
|
121
|
+
binItem.result = { success: true }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
resultMessages.push({ binItem, message: resultMessage })
|
|
125
|
+
|
|
126
|
+
if (transferStateId) {
|
|
127
|
+
const transferStateChange = {
|
|
128
|
+
transferId,
|
|
129
|
+
transferStateId,
|
|
130
|
+
reason
|
|
131
|
+
}
|
|
132
|
+
transferStateChanges.push(transferStateChange)
|
|
133
|
+
Logger.isDebugEnabled && Logger.debug(`processPositionFulfilBin::transferStateChange: ${JSON.stringify(transferStateChange)}`)
|
|
134
|
+
|
|
135
|
+
accumulatedTransferStatesCopy[transferId] = transferStateId
|
|
136
|
+
Logger.isDebugEnabled && Logger.debug(`processPositionFulfilBin::accumulatedTransferStatesCopy:finalizedTransferState ${JSON.stringify(transferStateId)}`)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
accumulatedPositionValue: runningPosition.toNumber(),
|
|
143
|
+
accumulatedTransferStates: accumulatedTransferStatesCopy, // finalized transfer state after fulfil processing
|
|
144
|
+
accumulatedPositionReservedValue, // not used but kept for consistency
|
|
145
|
+
accumulatedTransferStateChanges: transferStateChanges, // transfer state changes to be persisted in order
|
|
146
|
+
accumulatedPositionChanges: participantPositionChanges, // participant position changes to be persisted in order
|
|
147
|
+
notifyMessages: resultMessages // array of objects containing bin item and result message. {binItem, message}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
processPositionFulfilBin
|
|
153
|
+
}
|
|
@@ -665,8 +665,25 @@ const fulfil = async (error, messages) => {
|
|
|
665
665
|
await TransferService.handlePayeeResponse(transferId, payload, action)
|
|
666
666
|
const eventDetail = { functionality: TransferEventType.POSITION, action }
|
|
667
667
|
// Key position fulfil message with payee account id
|
|
668
|
+
let topicNameOverride
|
|
669
|
+
if (action === TransferEventAction.COMMIT) {
|
|
670
|
+
topicNameOverride = Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.COMMIT
|
|
671
|
+
} else if (action === TransferEventAction.RESERVE) {
|
|
672
|
+
topicNameOverride = Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.RESERVE
|
|
673
|
+
} else if (action === TransferEventAction.BULK_COMMIT) {
|
|
674
|
+
topicNameOverride = Config.KAFKA_CONFIG.EVENT_TYPE_ACTION_TOPIC_MAP?.POSITION?.BULK_COMMIT
|
|
675
|
+
}
|
|
668
676
|
const payeeAccount = await Participant.getAccountByNameAndCurrency(transfer.payeeFsp, transfer.currency, Enum.Accounts.LedgerAccountType.POSITION)
|
|
669
|
-
await Kafka.proceed(
|
|
677
|
+
await Kafka.proceed(
|
|
678
|
+
Config.KAFKA_CONFIG,
|
|
679
|
+
params,
|
|
680
|
+
{
|
|
681
|
+
consumerCommit,
|
|
682
|
+
eventDetail,
|
|
683
|
+
messageKey: payeeAccount.participantCurrencyId.toString(),
|
|
684
|
+
topicNameOverride
|
|
685
|
+
}
|
|
686
|
+
)
|
|
670
687
|
histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
|
|
671
688
|
return true
|
|
672
689
|
}
|
|
@@ -104,6 +104,33 @@ const updateParticipantPosition = async (trx, participantPositionId, participant
|
|
|
104
104
|
})
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
const getTransferInfoList = async (trx, transferIds, transferParticipantRoleTypeId, ledgerEntryTypeId) => {
|
|
108
|
+
try {
|
|
109
|
+
const knex = await Db.getKnex()
|
|
110
|
+
const transferInfos = await knex('transferParticipant')
|
|
111
|
+
.transacting(trx)
|
|
112
|
+
.where({
|
|
113
|
+
'transferParticipant.transferParticipantRoleTypeId': transferParticipantRoleTypeId,
|
|
114
|
+
'transferParticipant.ledgerEntryTypeId': ledgerEntryTypeId
|
|
115
|
+
})
|
|
116
|
+
.whereIn('transferParticipant.transferId', transferIds)
|
|
117
|
+
.select(
|
|
118
|
+
'transferParticipant.*'
|
|
119
|
+
)
|
|
120
|
+
const info = {}
|
|
121
|
+
// This should key the transfer info with the latest transferStateChangeId
|
|
122
|
+
for (const transferInfo of transferInfos) {
|
|
123
|
+
if (!(transferInfo.transferId in info)) {
|
|
124
|
+
info[transferInfo.transferId] = transferInfo
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return info
|
|
128
|
+
} catch (err) {
|
|
129
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
130
|
+
throw err
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
107
134
|
const bulkInsertTransferStateChanges = async (trx, transferStateChangeList) => {
|
|
108
135
|
const knex = await Db.getKnex()
|
|
109
136
|
return await knex.batchInsert('transferStateChange', transferStateChangeList).transacting(trx)
|
|
@@ -121,5 +148,6 @@ module.exports = {
|
|
|
121
148
|
updateParticipantPosition,
|
|
122
149
|
bulkInsertTransferStateChanges,
|
|
123
150
|
bulkInsertParticipantPositionChanges,
|
|
124
|
-
getAllParticipantCurrency
|
|
151
|
+
getAllParticipantCurrency,
|
|
152
|
+
getTransferInfoList
|
|
125
153
|
}
|