@centrifuge/sdk 0.55.1 → 0.55.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/entities/BalanceSheet.js +3 -3
  2. package/dist/entities/BalanceSheet.js.map +1 -1
  3. package/dist/entities/IndexerQueries/assetSnapshots.d.ts +69 -0
  4. package/dist/entities/IndexerQueries/assetSnapshots.d.ts.map +1 -0
  5. package/dist/entities/IndexerQueries/assetSnapshots.js +91 -0
  6. package/dist/entities/IndexerQueries/assetSnapshots.js.map +1 -0
  7. package/dist/entities/IndexerQueries/assetTransactions.d.ts +93 -0
  8. package/dist/entities/IndexerQueries/assetTransactions.d.ts.map +1 -0
  9. package/dist/entities/IndexerQueries/assetTransactions.js +75 -0
  10. package/dist/entities/IndexerQueries/assetTransactions.js.map +1 -0
  11. package/dist/entities/IndexerQueries/epochs.d.ts +45 -0
  12. package/dist/entities/IndexerQueries/epochs.d.ts.map +1 -0
  13. package/dist/entities/IndexerQueries/epochs.js +51 -0
  14. package/dist/entities/IndexerQueries/epochs.js.map +1 -0
  15. package/dist/entities/IndexerQueries/index.d.ts +40 -0
  16. package/dist/entities/IndexerQueries/index.d.ts.map +1 -0
  17. package/dist/entities/IndexerQueries/index.js +45 -0
  18. package/dist/entities/IndexerQueries/index.js.map +1 -0
  19. package/dist/entities/IndexerQueries/investorTransactions.d.ts +50 -0
  20. package/dist/entities/IndexerQueries/investorTransactions.d.ts.map +1 -0
  21. package/dist/entities/IndexerQueries/investorTransactions.js +55 -0
  22. package/dist/entities/IndexerQueries/investorTransactions.js.map +1 -0
  23. package/dist/entities/IndexerQueries/poolFeeSnapshots.d.ts +48 -0
  24. package/dist/entities/IndexerQueries/poolFeeSnapshots.d.ts.map +1 -0
  25. package/dist/entities/IndexerQueries/poolFeeSnapshots.js +62 -0
  26. package/dist/entities/IndexerQueries/poolFeeSnapshots.js.map +1 -0
  27. package/dist/entities/IndexerQueries/poolFeeTransactions.d.ts +34 -0
  28. package/dist/entities/IndexerQueries/poolFeeTransactions.d.ts.map +1 -0
  29. package/dist/entities/IndexerQueries/poolFeeTransactions.js +37 -0
  30. package/dist/entities/IndexerQueries/poolFeeTransactions.js.map +1 -0
  31. package/dist/entities/IndexerQueries/poolSnapshots.d.ts +79 -0
  32. package/dist/entities/IndexerQueries/poolSnapshots.d.ts.map +1 -0
  33. package/dist/entities/IndexerQueries/poolSnapshots.js +112 -0
  34. package/dist/entities/IndexerQueries/poolSnapshots.js.map +1 -0
  35. package/dist/entities/IndexerQueries/trancheCurrencyBalance.d.ts +76 -0
  36. package/dist/entities/IndexerQueries/trancheCurrencyBalance.d.ts.map +1 -0
  37. package/dist/entities/IndexerQueries/trancheCurrencyBalance.js +63 -0
  38. package/dist/entities/IndexerQueries/trancheCurrencyBalance.js.map +1 -0
  39. package/dist/entities/IndexerQueries/trancheSnapshots.d.ts +70 -0
  40. package/dist/entities/IndexerQueries/trancheSnapshots.d.ts.map +1 -0
  41. package/dist/entities/IndexerQueries/trancheSnapshots.js +76 -0
  42. package/dist/entities/IndexerQueries/trancheSnapshots.js.map +1 -0
  43. package/dist/entities/Reports/Processor.d.ts +46 -0
  44. package/dist/entities/Reports/Processor.d.ts.map +1 -0
  45. package/dist/entities/Reports/Processor.js +471 -0
  46. package/dist/entities/Reports/Processor.js.map +1 -0
  47. package/dist/entities/Reports/Processor.test.d.ts +2 -0
  48. package/dist/entities/Reports/Processor.test.d.ts.map +1 -0
  49. package/dist/entities/Reports/Processor.test.js +767 -0
  50. package/dist/entities/Reports/Processor.test.js.map +1 -0
  51. package/dist/entities/Reports/Reports.test.d.ts +2 -0
  52. package/dist/entities/Reports/Reports.test.d.ts.map +1 -0
  53. package/dist/entities/Reports/Reports.test.js +559 -0
  54. package/dist/entities/Reports/Reports.test.js.map +1 -0
  55. package/dist/entities/Reports/index.d.ts +21 -0
  56. package/dist/entities/Reports/index.d.ts.map +1 -0
  57. package/dist/entities/Reports/index.js +144 -0
  58. package/dist/entities/Reports/index.js.map +1 -0
  59. package/dist/entities/ShareClass.d.ts +2 -0
  60. package/dist/entities/ShareClass.d.ts.map +1 -1
  61. package/dist/entities/ShareClass.js +5 -1
  62. package/dist/entities/ShareClass.js.map +1 -1
  63. package/dist/tests/mocks/mockAssetSnapshots.d.ts +3 -0
  64. package/dist/tests/mocks/mockAssetSnapshots.d.ts.map +1 -0
  65. package/dist/tests/mocks/mockAssetSnapshots.js +56 -0
  66. package/dist/tests/mocks/mockAssetSnapshots.js.map +1 -0
  67. package/dist/tests/mocks/mockAssetTransactions.d.ts +3 -0
  68. package/dist/tests/mocks/mockAssetTransactions.d.ts.map +1 -0
  69. package/dist/tests/mocks/mockAssetTransactions.js +86 -0
  70. package/dist/tests/mocks/mockAssetTransactions.js.map +1 -0
  71. package/dist/tests/mocks/mockEpochs.d.ts +3 -0
  72. package/dist/tests/mocks/mockEpochs.d.ts.map +1 -0
  73. package/dist/tests/mocks/mockEpochs.js +26 -0
  74. package/dist/tests/mocks/mockEpochs.js.map +1 -0
  75. package/dist/tests/mocks/mockInvestorCurrencyBalance.d.ts +3 -0
  76. package/dist/tests/mocks/mockInvestorCurrencyBalance.d.ts.map +1 -0
  77. package/dist/tests/mocks/mockInvestorCurrencyBalance.js +29 -0
  78. package/dist/tests/mocks/mockInvestorCurrencyBalance.js.map +1 -0
  79. package/dist/tests/mocks/mockInvestorTransactions.d.ts +3 -0
  80. package/dist/tests/mocks/mockInvestorTransactions.d.ts.map +1 -0
  81. package/dist/tests/mocks/mockInvestorTransactions.js +35 -0
  82. package/dist/tests/mocks/mockInvestorTransactions.js.map +1 -0
  83. package/dist/tests/mocks/mockPoolFeeSnapshot.d.ts +3 -0
  84. package/dist/tests/mocks/mockPoolFeeSnapshot.d.ts.map +1 -0
  85. package/dist/tests/mocks/mockPoolFeeSnapshot.js +68 -0
  86. package/dist/tests/mocks/mockPoolFeeSnapshot.js.map +1 -0
  87. package/dist/tests/mocks/mockPoolFeeTransactions.d.ts +11 -0
  88. package/dist/tests/mocks/mockPoolFeeTransactions.d.ts.map +1 -0
  89. package/dist/tests/mocks/mockPoolFeeTransactions.js +20 -0
  90. package/dist/tests/mocks/mockPoolFeeTransactions.js.map +1 -0
  91. package/dist/tests/mocks/mockPoolSnapshots.d.ts +3 -0
  92. package/dist/tests/mocks/mockPoolSnapshots.d.ts.map +1 -0
  93. package/dist/tests/mocks/mockPoolSnapshots.js +66 -0
  94. package/dist/tests/mocks/mockPoolSnapshots.js.map +1 -0
  95. package/dist/tests/mocks/mockTrancheSnapshots.d.ts +3 -0
  96. package/dist/tests/mocks/mockTrancheSnapshots.d.ts.map +1 -0
  97. package/dist/tests/mocks/mockTrancheSnapshots.js +112 -0
  98. package/dist/tests/mocks/mockTrancheSnapshots.js.map +1 -0
  99. package/dist/types/reports.d.ts +336 -0
  100. package/dist/types/reports.d.ts.map +1 -0
  101. package/dist/types/reports.js +2 -0
  102. package/dist/types/reports.js.map +1 -0
  103. package/package.json +1 -1
@@ -0,0 +1,767 @@
1
+ import { expect } from 'chai';
2
+ import { mockAssetSnapshots } from '../../tests/mocks/mockAssetSnapshots.js';
3
+ import { mockAssetTransactions } from '../../tests/mocks/mockAssetTransactions.js';
4
+ import { mockEpochs } from '../../tests/mocks/mockEpochs.js';
5
+ import { mockInvestorCurrencyBalances } from '../../tests/mocks/mockInvestorCurrencyBalance.js';
6
+ import { mockInvestorTransactions } from '../../tests/mocks/mockInvestorTransactions.js';
7
+ import { mockPoolFeeSnapshots } from '../../tests/mocks/mockPoolFeeSnapshot.js';
8
+ import { mockFeeTransactions } from '../../tests/mocks/mockPoolFeeTransactions.js';
9
+ import { mockPoolMetadata } from '../../tests/mocks/mockPoolMetadata.js';
10
+ import { mockPoolSnapshots } from '../../tests/mocks/mockPoolSnapshots.js';
11
+ import { mockTrancheSnapshots } from '../../tests/mocks/mockTrancheSnapshots.js';
12
+ import { Balance, Price } from '../../utils/BigInt.js';
13
+ import { processor } from './Processor.js';
14
+ describe('Processor', () => {
15
+ describe('balanceSheet processor', () => {
16
+ it('should return empty array when no pool snapshots found', () => {
17
+ expect(processor.balanceSheet({
18
+ poolSnapshots: [],
19
+ trancheSnapshots: {},
20
+ })).to.deep.equal([]);
21
+ });
22
+ it('should process pool and tranche data correctly', () => {
23
+ const result = processor.balanceSheet({
24
+ poolSnapshots: mockPoolSnapshots,
25
+ trancheSnapshots: mockTrancheSnapshots,
26
+ });
27
+ expect(result).to.have.lengthOf(2);
28
+ const report = result[0];
29
+ expect(report.timestamp).to.equal('2024-01-01T12:00:00Z');
30
+ expect(report.assetValuation.toBigInt()).to.equal(0n);
31
+ expect(report.onchainReserve.toBigInt()).to.equal(0n);
32
+ expect(report.offchainCash.toBigInt()).to.equal(0n);
33
+ expect(report.accruedFees.toBigInt()).to.equal(0n);
34
+ expect(report.netAssetValue.toBigInt()).to.equal(0n);
35
+ expect(report.tranches.length).to.equal(2);
36
+ const seniorTranche = report.tranches.find((t) => t.tokenId === 'senior');
37
+ const juniorTranche = report.tranches.find((t) => t.tokenId === 'junior');
38
+ expect(seniorTranche?.tokenPrice.toBigInt()).to.equal(1000000000000000000n);
39
+ expect(juniorTranche?.tokenPrice.toBigInt()).to.equal(1120000000000000000n);
40
+ expect(report.totalCapital?.toBigInt()).to.equal(0n);
41
+ });
42
+ it('should group data by day when specified', () => {
43
+ const result = processor.balanceSheet({
44
+ poolSnapshots: mockPoolSnapshots,
45
+ trancheSnapshots: mockTrancheSnapshots,
46
+ }, { groupBy: 'day' });
47
+ expect(result).to.have.lengthOf(2);
48
+ expect(result?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01');
49
+ expect(result?.[1]?.timestamp.slice(0, 10)).to.equal('2024-01-02');
50
+ });
51
+ it('should group data by month when specified', () => {
52
+ const result = processor.balanceSheet({
53
+ poolSnapshots: mockPoolSnapshots,
54
+ trancheSnapshots: mockTrancheSnapshots,
55
+ }, { groupBy: 'month' });
56
+ expect(result).to.have.lengthOf(1);
57
+ expect(result?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-02');
58
+ });
59
+ });
60
+ describe('cashflow processor', () => {
61
+ const mockCashflowPoolSnapshots = [
62
+ {
63
+ ...mockPoolSnapshots[0],
64
+ id: 'pool-10',
65
+ timestamp: '2024-01-01T12:00:00Z',
66
+ sumPrincipalRepaidAmountByPeriod: Balance.fromFloat(1, 6), // 1.0
67
+ sumInterestRepaidAmountByPeriod: Balance.fromFloat(0.05, 6), // 0.05
68
+ sumBorrowedAmountByPeriod: Balance.fromFloat(2, 6), // 2.0
69
+ },
70
+ {
71
+ ...mockPoolSnapshots[0],
72
+ id: 'pool-11',
73
+ timestamp: '2024-01-01T18:00:00Z', // Same day, different time
74
+ sumPrincipalRepaidAmountByPeriod: Balance.fromFloat(0.5, 6), // 0.5
75
+ sumInterestRepaidAmountByPeriod: Balance.fromFloat(0.025, 6), // 0.025
76
+ sumBorrowedAmountByPeriod: Balance.fromFloat(1, 6), // 1.0
77
+ },
78
+ {
79
+ ...mockPoolSnapshots[0],
80
+ id: 'pool-12',
81
+ timestamp: '2024-01-02T12:00:00Z', // Next day
82
+ sumPrincipalRepaidAmountByPeriod: Balance.fromFloat(2, 6), // 2.0
83
+ sumInterestRepaidAmountByPeriod: Balance.fromFloat(0.1, 6), // 0.1
84
+ sumBorrowedAmountByPeriod: Balance.fromFloat(4, 6), // 4.0
85
+ },
86
+ ];
87
+ const mockCashflowFeeSnapshots = {
88
+ '2024-01-01': [
89
+ {
90
+ ...mockPoolFeeSnapshots['2024-01-01']?.[0],
91
+ poolFee: { name: 'serviceFee' },
92
+ sumPaidAmountByPeriod: new Balance(1000000n, 6), // 1.0
93
+ sumAccruedAmountByPeriod: new Balance(500000n, 6), // 0.5
94
+ },
95
+ {
96
+ ...mockPoolFeeSnapshots['2024-01-01']?.[1],
97
+ poolFee: { name: 'adminFee' },
98
+ sumPaidAmountByPeriod: new Balance(2000000n, 6), // 2.0
99
+ sumAccruedAmountByPeriod: new Balance(1000000n, 6), // 1.0
100
+ },
101
+ ],
102
+ '2024-01-02': [
103
+ {
104
+ ...mockPoolFeeSnapshots['2024-01-02']?.[0],
105
+ poolFee: { name: 'serviceFee' },
106
+ sumPaidAmountByPeriod: new Balance(3000000n, 6), // 3.0
107
+ sumAccruedAmountByPeriod: new Balance(1500000n, 6), // 1.5
108
+ },
109
+ {
110
+ ...mockPoolFeeSnapshots['2024-01-02']?.[1],
111
+ poolFee: { name: 'adminFee' },
112
+ sumPaidAmountByPeriod: new Balance(4000000n, 6), // 4.0
113
+ sumAccruedAmountByPeriod: new Balance(2000000n, 6), // 2.0
114
+ },
115
+ ],
116
+ };
117
+ it('should return empty array when no snapshots found', () => {
118
+ expect(processor.cashflow({ poolSnapshots: [], poolFeeSnapshots: {}, metadata: undefined })).to.deep.equal([]);
119
+ });
120
+ it('should throw an error if no metadata is passed', () => {
121
+ let thrown = false;
122
+ try {
123
+ processor.cashflow({
124
+ poolSnapshots: mockPoolSnapshots,
125
+ poolFeeSnapshots: mockPoolFeeSnapshots,
126
+ metadata: undefined,
127
+ });
128
+ }
129
+ catch {
130
+ thrown = true;
131
+ }
132
+ expect(thrown).to.be.true;
133
+ });
134
+ it('should aggregate values correctly when grouping by day', () => {
135
+ const result = processor.cashflow({
136
+ poolSnapshots: mockCashflowPoolSnapshots,
137
+ poolFeeSnapshots: mockCashflowFeeSnapshots,
138
+ metadata: mockPoolMetadata,
139
+ }, { groupBy: 'day' });
140
+ expect(result).to.have.lengthOf(2);
141
+ const jan1 = result[0];
142
+ expect(jan1?.timestamp.slice(0, 10)).to.equal('2024-01-01');
143
+ expect(jan1?.principalPayments.toFloat()).to.equal(1.5); // 1.0 + 0.5
144
+ expect(jan1?.interestPayments.toFloat()).to.equal(0.075); // 0.05 + 0.025
145
+ expect(jan1?.subtype === 'privateCredit' ? jan1?.assetFinancing?.toFloat() : jan1?.assetPurchases?.toFloat()).to.equal(3); // 2.0 + 1.0
146
+ expect(jan1?.fees.length).to.equal(2);
147
+ expect(jan1?.fees[0]?.name).to.equal('serviceFee');
148
+ expect(jan1?.fees[1]?.name).to.equal('adminFee');
149
+ expect(jan1?.fees[0]?.amount.toFloat()).to.equal(1);
150
+ expect(jan1?.fees[1]?.amount.toFloat()).to.equal(2);
151
+ expect(jan1?.fees[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01');
152
+ expect(jan1?.fees[1]?.timestamp.slice(0, 10)).to.equal('2024-01-01');
153
+ const jan2 = result[1];
154
+ expect(jan2?.timestamp.slice(0, 10)).to.equal('2024-01-02');
155
+ expect(jan2?.principalPayments.toFloat()).to.equal(2); // 2.0
156
+ expect(jan2?.interestPayments.toFloat()).to.equal(0.1); // 0.1
157
+ expect(jan2?.subtype === 'privateCredit' ? jan2?.assetFinancing?.toFloat() : jan2?.assetPurchases?.toFloat()).to.equal(4); // 4.0
158
+ expect(jan2?.fees.length).to.equal(2);
159
+ expect(jan2?.fees[0]?.name).to.equal('serviceFee');
160
+ expect(jan2?.fees[1]?.name).to.equal('adminFee');
161
+ expect(jan2?.fees[0]?.amount.toFloat()).to.equal(3);
162
+ expect(jan2?.fees[1]?.amount.toFloat()).to.equal(4);
163
+ expect(jan2?.fees[0]?.timestamp.slice(0, 10)).to.equal('2024-01-02');
164
+ expect(jan2?.fees[1]?.timestamp.slice(0, 10)).to.equal('2024-01-02');
165
+ });
166
+ it('should aggregate values correctly when grouping by month', () => {
167
+ const result = processor.cashflow({
168
+ poolSnapshots: mockCashflowPoolSnapshots,
169
+ poolFeeSnapshots: mockCashflowFeeSnapshots,
170
+ metadata: mockPoolMetadata,
171
+ }, { groupBy: 'month' });
172
+ expect(result).to.have.lengthOf(1);
173
+ const january = result[0];
174
+ expect(january?.timestamp.slice(0, 10)).to.equal('2024-01-02'); // Grouping by month returns the last day of the period
175
+ expect(january?.principalPayments.toFloat()).to.equal(3.5); // 1.0 + 0.5 + 2.0
176
+ expect(january?.interestPayments.toFloat()).to.equal(0.175); // 0.05 + 0.025 + 0.1
177
+ expect(january?.subtype === 'privateCredit' ? january?.assetFinancing?.toFloat() : january?.assetPurchases?.toFloat()).to.equal(7); // 2.0 + 1.0 + 4.0
178
+ expect(january?.fees.length).to.equal(2);
179
+ expect(january?.fees[0]?.name).to.equal('serviceFee');
180
+ expect(january?.fees[1]?.name).to.equal('adminFee');
181
+ // fees are NOT aggregated by period
182
+ expect(january?.fees[0]?.amount.toFloat()).to.equal(3);
183
+ expect(january?.fees[1]?.amount.toFloat()).to.equal(4);
184
+ });
185
+ it('should return realizedPL if pool is public credit', () => {
186
+ const result = processor.cashflow({
187
+ poolSnapshots: mockCashflowPoolSnapshots,
188
+ poolFeeSnapshots: mockCashflowFeeSnapshots,
189
+ metadata: {
190
+ ...mockPoolMetadata,
191
+ pool: { ...mockPoolMetadata.pool, asset: { ...mockPoolMetadata.pool.asset, class: 'Public credit' } },
192
+ },
193
+ }, { groupBy: 'day' });
194
+ expect(result?.[0]).to.have.property('realizedPL');
195
+ });
196
+ });
197
+ describe('profit and loss processor', () => {
198
+ const mockPLPoolSnapshots = [
199
+ {
200
+ ...mockPoolSnapshots[0],
201
+ id: 'pool-10',
202
+ timestamp: '2024-01-01T12:00:00Z',
203
+ sumInterestRepaidAmountByPeriod: Balance.fromFloat(0.05, 6), // 0.05
204
+ sumInterestAccruedByPeriod: Balance.fromFloat(0.1, 6), // 0.1
205
+ sumDebtWrittenOffByPeriod: Balance.fromFloat(0.02, 6), // 0.02
206
+ sumUnscheduledRepaidAmountByPeriod: Balance.fromFloat(0.01, 6), // 0.01
207
+ sumUnrealizedProfitByPeriod: Balance.fromFloat(0.15, 6), // 0.15
208
+ },
209
+ {
210
+ ...mockPoolSnapshots[1],
211
+ id: 'pool-11',
212
+ timestamp: '2024-01-02T12:00:00Z',
213
+ sumInterestRepaidAmountByPeriod: Balance.fromFloat(0.1, 6),
214
+ sumInterestAccruedByPeriod: Balance.fromFloat(0.2, 6),
215
+ sumDebtWrittenOffByPeriod: Balance.fromFloat(0.03, 6),
216
+ sumUnscheduledRepaidAmountByPeriod: Balance.fromFloat(0.02, 6),
217
+ sumUnrealizedProfitByPeriod: Balance.fromFloat(0.25, 6),
218
+ },
219
+ ];
220
+ const mockPLFeeSnapshots = {
221
+ '2024-01-01': [
222
+ {
223
+ poolFee: { name: 'serviceFee' },
224
+ sumAccruedAmountByPeriod: Balance.fromFloat(0.01, 6),
225
+ sumChargedAmountByPeriod: Balance.fromFloat(0.02, 6),
226
+ timestamp: '2024-01-01T12:00:00Z',
227
+ poolFeeId: 'pool-fee-1',
228
+ },
229
+ ],
230
+ '2024-01-02': [
231
+ {
232
+ poolFee: { name: 'serviceFee' },
233
+ sumAccruedAmountByPeriod: Balance.fromFloat(0.02, 6),
234
+ sumChargedAmountByPeriod: Balance.fromFloat(0.03, 6),
235
+ timestamp: '2024-01-02T12:00:00Z',
236
+ poolFeeId: 'pool-fee-2',
237
+ },
238
+ ],
239
+ };
240
+ it('should return empty array when no snapshots found', () => {
241
+ expect(processor.profitAndLoss({ poolSnapshots: [], poolFeeSnapshots: {}, metadata: undefined })).to.deep.equal([]);
242
+ });
243
+ it('should handle undefined metadata', () => {
244
+ let thrown = false;
245
+ try {
246
+ processor.profitAndLoss({
247
+ poolSnapshots: mockPLPoolSnapshots,
248
+ poolFeeSnapshots: mockPLFeeSnapshots,
249
+ metadata: undefined,
250
+ });
251
+ }
252
+ catch {
253
+ thrown = true;
254
+ }
255
+ expect(thrown).to.be.true;
256
+ });
257
+ it('should process private credit pool data correctly', () => {
258
+ const result = processor.profitAndLoss({
259
+ poolSnapshots: mockPLPoolSnapshots,
260
+ poolFeeSnapshots: mockPLFeeSnapshots,
261
+ metadata: mockPoolMetadata, // defaults to private credit
262
+ });
263
+ expect(result).to.have.lengthOf(2);
264
+ const firstDay = result[0];
265
+ expect(firstDay?.subtype).to.equal('privateCredit');
266
+ expect(firstDay).to.have.property('interestAccrued');
267
+ expect(firstDay).to.have.property('assetWriteOffs');
268
+ expect(firstDay?.interestAccrued.toFloat()).to.equal(0.1);
269
+ expect(firstDay?.assetWriteOffs.toFloat()).to.equal(0.02);
270
+ expect(firstDay?.interestPayments.toFloat()).to.equal(0.05);
271
+ expect(firstDay?.otherPayments.toFloat()).to.equal(0.01);
272
+ expect(firstDay?.profitAndLossFromAsset.toFloat()).to.equal(0.16); // 0.05 + 0.1 + 0.02 - 0.01
273
+ });
274
+ it('should process public credit pool correctly', () => {
275
+ const result = processor.profitAndLoss({
276
+ poolSnapshots: mockPLPoolSnapshots,
277
+ poolFeeSnapshots: mockPLFeeSnapshots,
278
+ metadata: {
279
+ ...mockPoolMetadata,
280
+ pool: {
281
+ ...mockPoolMetadata.pool,
282
+ asset: { ...mockPoolMetadata.pool.asset, class: 'Public credit' },
283
+ },
284
+ },
285
+ });
286
+ expect(result).to.have.lengthOf(2);
287
+ const firstDay = result[0];
288
+ expect(firstDay?.subtype).to.equal('publicCredit');
289
+ expect(firstDay?.profitAndLossFromAsset.toFloat()).to.equal(0.15); // unrealized profit
290
+ expect(firstDay?.interestPayments.toFloat()).to.equal(0.05);
291
+ expect(firstDay?.otherPayments.toFloat()).to.equal(0.01);
292
+ expect(firstDay).to.have.property('totalIncome');
293
+ expect(firstDay?.totalIncome.toFloat()).to.equal(0.21); // 0.15 + 0.05 + 0.01
294
+ });
295
+ it('should aggregate values correctly when grouping by month', () => {
296
+ const result = processor.profitAndLoss({
297
+ poolSnapshots: mockPLPoolSnapshots,
298
+ poolFeeSnapshots: mockPLFeeSnapshots,
299
+ metadata: mockPoolMetadata,
300
+ }, { groupBy: 'month' });
301
+ expect(result).to.have.lengthOf(1);
302
+ const january = result[0];
303
+ expect(january?.timestamp.slice(0, 10)).to.equal('2024-01-02');
304
+ expect(january?.interestPayments.toFloat()).to.equal(0.15); // 0.05 + 0.1
305
+ expect(january?.otherPayments.toFloat()).to.equal(0.03); // 0.01 + 0.02
306
+ });
307
+ it('should make sure timestamp for pool state and pool fee match', () => {
308
+ const result = processor.profitAndLoss({
309
+ poolSnapshots: mockPLPoolSnapshots,
310
+ poolFeeSnapshots: mockPLFeeSnapshots,
311
+ metadata: mockPoolMetadata,
312
+ });
313
+ expect(result[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01');
314
+ expect(result[0]?.fees?.[0]?.timestamp.slice(0, 10)).to.equal('2024-01-01');
315
+ });
316
+ });
317
+ describe('investor transactions processor', () => {
318
+ it('should return empty array when no transactions found', () => {
319
+ expect(processor.investorTransactions({ investorTransactions: [] })).to.deep.equal([]);
320
+ });
321
+ it('should process investor transactions correctly without filters', () => {
322
+ const result = processor.investorTransactions({
323
+ investorTransactions: mockInvestorTransactions,
324
+ });
325
+ expect(result).to.have.lengthOf(2);
326
+ const firstTx = result[0];
327
+ expect(firstTx?.timestamp.slice(0, 10)).to.equal('2024-01-01');
328
+ expect(firstTx?.chainId).to.equal(1);
329
+ expect(firstTx?.account).to.equal('0x123a');
330
+ expect(firstTx?.epoch).to.equal('1');
331
+ expect(firstTx?.transactionType).to.equal('INVEST_ORDER_UPDATE');
332
+ expect(firstTx?.currencyAmount.toFloat()).to.equal(1.0);
333
+ expect(firstTx?.trancheTokenAmount.toFloat()).to.equal(0.9);
334
+ expect(firstTx?.price.toString()).to.equal('1100000000000000000');
335
+ expect(firstTx?.transactionHash).to.equal('0xabc');
336
+ });
337
+ it('should filter by tokenId', () => {
338
+ const mockInvestorTransactionsWithJunior = [
339
+ ...mockInvestorTransactions,
340
+ {
341
+ ...mockInvestorTransactions[0],
342
+ trancheId: 'junior',
343
+ },
344
+ ];
345
+ const result = processor.investorTransactions({
346
+ investorTransactions: mockInvestorTransactionsWithJunior,
347
+ }, { tokenId: 'senior' });
348
+ expect(result).to.have.lengthOf(2);
349
+ expect(result[0]?.trancheTokenId).to.equal('senior');
350
+ });
351
+ it('should filter by address', () => {
352
+ const result = processor.investorTransactions({
353
+ investorTransactions: mockInvestorTransactions,
354
+ }, { address: '0x123a' });
355
+ expect(result).to.have.lengthOf(1);
356
+ expect(result[0]?.account).to.equal('0x123a');
357
+ });
358
+ it('should filter by network', () => {
359
+ const mockInvestorTransactionsWithNetwork = [
360
+ ...mockInvestorTransactions,
361
+ {
362
+ ...mockInvestorTransactions[0],
363
+ chainId: 2,
364
+ },
365
+ ];
366
+ const result = processor.investorTransactions({
367
+ investorTransactions: mockInvestorTransactionsWithNetwork,
368
+ }, { network: 2 });
369
+ expect(result).to.have.lengthOf(1);
370
+ expect(result[0]?.chainId).to.equal(2);
371
+ });
372
+ it('should filter by all networks', () => {
373
+ const result = processor.investorTransactions({
374
+ investorTransactions: mockInvestorTransactions,
375
+ }, { network: 'all' });
376
+ expect(result).to.have.lengthOf(2);
377
+ });
378
+ it('should filter by centrifuge network', () => {
379
+ const mockInvestorTransactionsWithCentrifuge = [
380
+ ...mockInvestorTransactions,
381
+ {
382
+ ...mockInvestorTransactions[0],
383
+ chainId: 'centrifuge',
384
+ },
385
+ ];
386
+ const result = processor.investorTransactions({
387
+ investorTransactions: mockInvestorTransactionsWithCentrifuge,
388
+ }, { network: 'centrifuge' });
389
+ expect(result).to.have.lengthOf(1);
390
+ expect(result[0]?.chainId).to.equal('centrifuge');
391
+ });
392
+ it('should filter by transaction type', () => {
393
+ const result = processor.investorTransactions({
394
+ investorTransactions: mockInvestorTransactions,
395
+ }, { transactionType: 'orders' });
396
+ expect(result).to.have.lengthOf(1);
397
+ expect(result[0]?.transactionType).to.equal('INVEST_ORDER_UPDATE');
398
+ });
399
+ it('should filter by network and transaction type', () => {
400
+ const mockInvestorTransactionsWithNetworkAndOrders = [
401
+ ...mockInvestorTransactions,
402
+ {
403
+ ...mockInvestorTransactions[0],
404
+ chainId: 2,
405
+ },
406
+ ];
407
+ const result = processor.investorTransactions({
408
+ investorTransactions: mockInvestorTransactionsWithNetworkAndOrders,
409
+ }, { network: 1, transactionType: 'orders' });
410
+ expect(result).to.have.lengthOf(1);
411
+ expect(result[0]?.chainId).to.equal(1);
412
+ expect(result[0]?.transactionType).to.equal('INVEST_ORDER_UPDATE');
413
+ });
414
+ it('should return an empty array when no filters match', () => {
415
+ const result = processor.investorTransactions({
416
+ investorTransactions: mockInvestorTransactions,
417
+ }, { network: 2, transactionType: 'executions' });
418
+ expect(result).to.deep.equal([]);
419
+ });
420
+ });
421
+ describe('asset transactions processor', () => {
422
+ it('should return empty array when no transactions found', () => {
423
+ expect(processor.assetTransactions({ assetTransactions: [] })).to.deep.equal([]);
424
+ });
425
+ it('should process asset transactions correctly without filters', () => {
426
+ const result = processor.assetTransactions({
427
+ assetTransactions: mockAssetTransactions,
428
+ });
429
+ expect(result).to.have.lengthOf(3);
430
+ });
431
+ it('should filter by assetId', () => {
432
+ const result = processor.assetTransactions({
433
+ assetTransactions: mockAssetTransactions,
434
+ }, { assetId: '1' });
435
+ expect(result).to.have.lengthOf(2);
436
+ });
437
+ it('should filter by transaction type', () => {
438
+ const types = [
439
+ { type: 'created', expected: 0 },
440
+ { type: 'financed', expected: 1 },
441
+ { type: 'repaid', expected: 1 },
442
+ { type: 'priced', expected: 0 },
443
+ { type: 'closed', expected: 0 },
444
+ { type: 'cashTransfer', expected: 1 },
445
+ ];
446
+ for (const { type, expected } of types) {
447
+ const result = processor.assetTransactions({
448
+ assetTransactions: mockAssetTransactions,
449
+ }, { transactionType: type });
450
+ expect(result).to.have.lengthOf(expected);
451
+ }
452
+ });
453
+ it('should make sure all the fields exist', () => {
454
+ const result = processor.assetTransactions({ assetTransactions: mockAssetTransactions });
455
+ expect(result).to.have.lengthOf(3);
456
+ expect(result[0]).to.have.property('type');
457
+ expect(result[0]).to.have.property('timestamp');
458
+ expect(result[0]).to.have.property('assetId');
459
+ expect(result[0]).to.have.property('transactionType');
460
+ expect(result[0]).to.have.property('amount');
461
+ expect(result[0]).to.have.property('epoch');
462
+ expect(result[0]).to.have.property('transactionHash');
463
+ expect(result[0]).to.have.property('fromAsset'); // optional
464
+ expect(result[1]).to.have.property('toAsset'); // optional
465
+ });
466
+ });
467
+ describe('fee transactions processor', () => {
468
+ it('should return empty array when no transactions found', () => {
469
+ expect(processor.feeTransactions({ poolFeeTransactions: [] })).to.deep.equal([]);
470
+ });
471
+ it('should process fee transactions correctly', () => {
472
+ const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions });
473
+ expect(result).to.have.lengthOf(2);
474
+ });
475
+ it('should filter by transaction type all', () => {
476
+ const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions }, { transactionType: 'all' });
477
+ expect(result).to.have.lengthOf(2);
478
+ });
479
+ it('should filter by transaction type accrued', () => {
480
+ const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions }, { transactionType: 'accrued' });
481
+ expect(result).to.have.lengthOf(1);
482
+ });
483
+ it('should filter by transaction type paid', () => {
484
+ const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions }, { transactionType: 'paid' });
485
+ expect(result).to.have.lengthOf(1);
486
+ });
487
+ it('should return an empty array when no filters match', () => {
488
+ const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions }, { transactionType: 'directChargeMade' });
489
+ expect(result).to.deep.equal([]);
490
+ });
491
+ });
492
+ describe('token price processor', () => {
493
+ it('should return empty array when no snapshots found', () => {
494
+ expect(processor.tokenPrice({ trancheSnapshots: {} })).to.deep.equal([]);
495
+ });
496
+ it('should process token price correctly', () => {
497
+ const result = processor.tokenPrice({ trancheSnapshots: mockTrancheSnapshots });
498
+ expect(result).to.have.lengthOf(2);
499
+ expect(result[0]?.tranches[0]?.price?.toDecimal().toString()).to.equal('1');
500
+ expect(result[0]?.tranches[0]?.id).to.equal('senior');
501
+ });
502
+ it('should group by month', () => {
503
+ const result = processor.tokenPrice({ trancheSnapshots: mockTrancheSnapshots }, { groupBy: 'month' });
504
+ expect(result).to.have.lengthOf(1);
505
+ });
506
+ it('should make sure all fields are present', () => {
507
+ const result = processor.tokenPrice({ trancheSnapshots: mockTrancheSnapshots }, { groupBy: 'day' });
508
+ expect(result).to.have.lengthOf(2);
509
+ expect(result[0]).to.have.property('tranches');
510
+ expect(result[0]).to.have.property('timestamp');
511
+ expect(result[0]?.tranches[0]).to.have.property('id');
512
+ expect(result[0]?.tranches[0]).to.have.property('yieldMTD');
513
+ expect(result[0]?.tranches[0]).to.have.property('yieldQTD');
514
+ expect(result[0]?.tranches[0]).to.have.property('yieldYTD');
515
+ expect(result[0]?.tranches[0]).to.have.property('yield7daysAnnualized');
516
+ expect(result[0]?.tranches[0]).to.have.property('yield30daysAnnualized');
517
+ expect(result[0]?.tranches[0]).to.have.property('yield90daysAnnualized');
518
+ expect(result[0]?.tranches[0]).to.have.property('timestamp');
519
+ expect(result[0]?.tranches[0]).to.have.property('supply');
520
+ expect(result[0]?.tranches[0]).to.have.property('price');
521
+ });
522
+ });
523
+ describe('asset list processor', () => {
524
+ it('should return empty array when no snapshots found', () => {
525
+ expect(processor.assetList({ assetSnapshots: [], metadata: undefined })).to.deep.equal([]);
526
+ });
527
+ it('should throw an error if no metadata is passed', () => {
528
+ let thrown = false;
529
+ try {
530
+ processor.assetList({ assetSnapshots: mockAssetSnapshots, metadata: undefined });
531
+ }
532
+ catch {
533
+ thrown = true;
534
+ }
535
+ expect(thrown).to.be.true;
536
+ });
537
+ it('should process asset list correctly', () => {
538
+ const result = processor.assetList({ assetSnapshots: mockAssetSnapshots, metadata: mockPoolMetadata });
539
+ expect(result).to.have.lengthOf(2);
540
+ });
541
+ it('should filter by status ongoing', () => {
542
+ const result = processor.assetList({ assetSnapshots: mockAssetSnapshots, metadata: mockPoolMetadata }, { status: 'ongoing' });
543
+ expect(result).to.have.lengthOf(2);
544
+ });
545
+ it('should filter by status repaid', () => {
546
+ const result = processor.assetList({ assetSnapshots: mockAssetSnapshots, metadata: mockPoolMetadata }, { status: 'repaid' });
547
+ expect(result).to.have.lengthOf(0);
548
+ });
549
+ it('should filter by status overdue', () => {
550
+ const mockAssetSnapshotsOverdue = [
551
+ ...mockAssetSnapshots,
552
+ { ...mockAssetSnapshots[0], actualMaturityDate: '2023-01-01' },
553
+ ];
554
+ const result = processor.assetList({ assetSnapshots: mockAssetSnapshotsOverdue, metadata: mockPoolMetadata }, { status: 'overdue' });
555
+ expect(result).to.have.lengthOf(1);
556
+ });
557
+ it('should return the correct data for private credit pools', () => {
558
+ const result = processor.assetList({ assetSnapshots: mockAssetSnapshots, metadata: mockPoolMetadata }, { status: 'ongoing' });
559
+ expect(result).to.have.lengthOf(2);
560
+ expect(result?.[0]).to.have.property('outstandingPrincipal');
561
+ expect(result?.[0]).to.have.property('outstandingInterest');
562
+ expect(result?.[0]).to.have.property('repaidPrincipal');
563
+ expect(result?.[0]).to.have.property('repaidInterest');
564
+ expect(result?.[0]).to.have.property('repaidUnscheduled');
565
+ expect(result?.[0]).to.have.property('originationDate');
566
+ expect(result?.[0]).to.have.property('maturityDate');
567
+ expect(result?.[0]).to.have.property('valuationMethod');
568
+ expect(result?.[0]).to.have.property('advanceRate');
569
+ expect(result?.[0]).to.have.property('collateralValue');
570
+ expect(result?.[0]).to.have.property('probabilityOfDefault');
571
+ expect(result?.[0]).to.have.property('lossGivenDefault');
572
+ expect(result?.[0]).to.have.property('discountRate');
573
+ expect(result?.[0]).to.have.property('name');
574
+ });
575
+ it('should return the correct data for public credit pools', () => {
576
+ const result = processor.assetList({
577
+ assetSnapshots: mockAssetSnapshots,
578
+ metadata: {
579
+ ...mockPoolMetadata,
580
+ pool: { ...mockPoolMetadata.pool, asset: { ...mockPoolMetadata.pool.asset, class: 'Public credit' } },
581
+ },
582
+ }, { status: 'ongoing' });
583
+ expect(result).to.have.lengthOf(2);
584
+ expect(result?.[0]).to.have.property('faceValue');
585
+ expect(result?.[0]).to.have.property('outstandingQuantity');
586
+ expect(result?.[0]).to.have.property('currentPrice');
587
+ expect(result?.[0]).to.have.property('unrealizedProfit');
588
+ expect(result?.[0]).to.have.property('realizedProfit');
589
+ expect(result?.[0]).to.have.property('name');
590
+ });
591
+ });
592
+ describe('investor list processor', () => {
593
+ it('should return empty array when no balances found', () => {
594
+ expect(processor.investorList({ trancheCurrencyBalance: [] })).to.deep.equal([]);
595
+ });
596
+ it('should filter by network', () => {
597
+ const result = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances }, { network: 1 });
598
+ expect(result).to.have.lengthOf(1);
599
+ });
600
+ it('should filter by centrifuge network', () => {
601
+ const result = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances }, { network: 'centrifuge' });
602
+ expect(result).to.have.lengthOf(1);
603
+ });
604
+ it('should filter by address', () => {
605
+ const result = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances }, { address: '0x123' });
606
+ expect(result).to.have.lengthOf(1);
607
+ });
608
+ it('should filter by trancheId', () => {
609
+ const result = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances }, { trancheId: 'tranche-1' });
610
+ expect(result).to.have.lengthOf(2);
611
+ const result2 = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances }, { trancheId: 'tranche-2' });
612
+ expect(result2).to.have.lengthOf(0);
613
+ });
614
+ it('should make sure all the fields exist', () => {
615
+ const result = processor.investorList({ trancheCurrencyBalance: mockInvestorCurrencyBalances });
616
+ expect(result).to.have.lengthOf(2);
617
+ expect(result[0]).to.have.property('type');
618
+ expect(result[0]).to.have.property('chainId');
619
+ expect(result[0]).to.have.property('accountId');
620
+ expect(result[0]).to.have.property('evmAddress');
621
+ expect(result[0]).to.have.property('position');
622
+ expect(result[0]).to.have.property('poolPercentage');
623
+ expect(result[0]).to.have.property('pendingInvest');
624
+ expect(result[0]).to.have.property('pendingRedeem');
625
+ expect(result[0]).to.have.property('trancheId');
626
+ });
627
+ });
628
+ describe('orders list processor', () => {
629
+ it('should return empty array when no epochs found', () => {
630
+ expect(processor.ordersList({ poolEpochs: [] })).to.deep.equal([]);
631
+ });
632
+ it('should process orders list correctly', () => {
633
+ const result = processor.ordersList({ poolEpochs: mockEpochs });
634
+ expect(result).to.have.lengthOf(2);
635
+ expect(result[0]?.epoch).to.equal('1');
636
+ expect(result[0]?.netAssetValue.toString()).to.equal(Balance.fromFloat(1000, 6).toString());
637
+ expect(result[0]?.navPerShare.toString()).to.equal(new Price(1000000000000000000n).toString());
638
+ expect(result[0]?.lockedInvestments.toString()).to.equal(Balance.fromFloat(1000, 6).toString());
639
+ expect(result[0]?.lockedRedemptions.toString()).to.equal(Balance.fromFloat(100, 6).toString());
640
+ expect(result[0]?.executedInvestments.toString()).to.equal(Balance.fromFloat(900, 6).toString());
641
+ expect(result[0]?.executedRedemptions.toString()).to.equal(Balance.fromFloat(90, 6).toString());
642
+ expect(result[0]?.paidFees.toString()).to.equal(Balance.fromFloat(100, 6).toString());
643
+ });
644
+ });
645
+ describe('asset time series processor', () => {
646
+ it('should return empty array when no snapshots found', () => {
647
+ expect(processor.assetTimeSeries({ assetSnapshots: [] })).to.deep.equal([]);
648
+ });
649
+ it('should process asset time series correctly', () => {
650
+ const result = processor.assetTimeSeries({ assetSnapshots: mockAssetSnapshots });
651
+ expect(result).to.have.lengthOf(2);
652
+ expect(result[0]?.assetId).to.equal('1');
653
+ expect(result[0]?.currentPrice.toString()).to.equal(Balance.fromFloat(1, 6).toString());
654
+ });
655
+ });
656
+ describe('applyGrouping', () => {
657
+ const applyGrouping = processor['applyGrouping'];
658
+ const mockData = [
659
+ { a: Balance.fromFloat(10, 6), timestamp: '2024-01-01' },
660
+ { a: Balance.fromFloat(20, 6), timestamp: '2024-01-01' },
661
+ ];
662
+ it('should return empty array when no items found', () => {
663
+ expect(applyGrouping([], 'day', 'sum')).to.deep.equal([]);
664
+ });
665
+ it('should return items by day when no grouping is specified', () => {
666
+ const latest = applyGrouping(mockData, undefined, 'latest');
667
+ expect(latest).to.deep.equal([{ a: Balance.fromFloat(20, 6), timestamp: '2024-01-01' }]);
668
+ const summed = applyGrouping(mockData, undefined, 'sum');
669
+ expect(summed).to.deep.equal([{ a: Balance.fromFloat(30, 6), timestamp: '2024-01-01' }]);
670
+ });
671
+ it('should return latest item when strategy is latest', () => {
672
+ const grouped = applyGrouping(mockData, 'day', 'latest');
673
+ expect(grouped).to.deep.equal([{ a: Balance.fromFloat(20, 6), timestamp: '2024-01-01' }]);
674
+ });
675
+ it('should aggregate values when strategy is sum', () => {
676
+ const grouped = applyGrouping(mockData, 'day', 'sum');
677
+ expect(grouped).to.deep.equal([{ a: Balance.fromFloat(30, 6), timestamp: '2024-01-01' }]);
678
+ });
679
+ it('should return latest item when strategy is latest and no grouping is specified', () => {
680
+ const grouped = applyGrouping(mockData, undefined, 'latest');
681
+ expect(grouped).to.deep.equal([{ a: Balance.fromFloat(20, 6), timestamp: '2024-01-01' }]);
682
+ });
683
+ it('should aggregate values when strategy is sum and no grouping is specified', () => {
684
+ const grouped = applyGrouping(mockData, undefined, 'sum');
685
+ expect(grouped).to.deep.equal([{ a: Balance.fromFloat(30, 6), timestamp: '2024-01-01' }]);
686
+ });
687
+ it('should return latest item when strategy is latest and grouping is month', () => {
688
+ const extendedMockData = [...mockData, { a: Balance.fromFloat(30, 6), timestamp: '2024-02-01' }];
689
+ const grouped = applyGrouping(extendedMockData, 'month', 'latest');
690
+ const expected = [
691
+ { a: Balance.fromFloat(20, 6), timestamp: '2024-01-01' },
692
+ { a: Balance.fromFloat(30, 6), timestamp: '2024-02-01' },
693
+ ];
694
+ expect(grouped).to.deep.equal(expected);
695
+ });
696
+ it('should aggregate values when strategy is sum and grouping is month (Token)', () => {
697
+ const extendedMockData = [
698
+ { a: Balance.fromFloat(10, 6), timestamp: '2024-01-01' },
699
+ { a: Balance.fromFloat(20, 6), timestamp: '2024-01-02' },
700
+ { a: Balance.fromFloat(30, 6), timestamp: '2024-02-01' },
701
+ ];
702
+ const grouped = applyGrouping(extendedMockData, 'month', 'sum');
703
+ const expected = [
704
+ { a: Balance.fromFloat(30, 6), timestamp: '2024-01-02' },
705
+ { a: Balance.fromFloat(30, 6), timestamp: '2024-02-01' },
706
+ ];
707
+ expect(grouped).to.deep.equal(expected);
708
+ });
709
+ it('should aggregate values when strategy is sum and grouping is month (Price)', () => {
710
+ const extendedMockData = [
711
+ { a: Price.fromFloat(10), timestamp: '2024-01-01' },
712
+ { a: Price.fromFloat(20), timestamp: '2024-01-02' },
713
+ { a: Price.fromFloat(30), timestamp: '2024-02-01' },
714
+ ];
715
+ const grouped = applyGrouping(extendedMockData, 'month', 'sum');
716
+ const expected = [
717
+ { a: Price.fromFloat(30), timestamp: '2024-01-02' },
718
+ { a: Price.fromFloat(30), timestamp: '2024-02-01' },
719
+ ];
720
+ expect(grouped).to.deep.equal(expected);
721
+ });
722
+ it('should only aggregate top-level Currency values and use last value for nested objects', () => {
723
+ const items = [
724
+ {
725
+ timestamp: '2024-01-01T12:00:00Z',
726
+ topLevelAmount: Balance.fromFloat(1, 6), // should be summed
727
+ nested: {
728
+ amount: Balance.fromFloat(1, 6), // should take last value
729
+ description: 'first',
730
+ },
731
+ fees: [
732
+ {
733
+ amount: Balance.fromFloat(0.5, 6), // should take last value
734
+ name: 'fee1',
735
+ },
736
+ ],
737
+ },
738
+ {
739
+ timestamp: '2024-01-01T18:00:00Z',
740
+ topLevelAmount: Balance.fromFloat(2, 6),
741
+ nested: {
742
+ amount: Balance.fromFloat(3, 6),
743
+ description: 'second',
744
+ },
745
+ fees: [
746
+ {
747
+ amount: Balance.fromFloat(0.7, 6),
748
+ name: 'fee1',
749
+ },
750
+ ],
751
+ },
752
+ ];
753
+ const result = processor['applyGrouping'](items, 'day', 'sum');
754
+ expect(result).to.have.lengthOf(1);
755
+ const aggregated = result[0];
756
+ // Top level Currency should be summed
757
+ expect(aggregated?.topLevelAmount.toFloat()).to.equal(3); // 1 + 2
758
+ // Nested Currency should be from last item
759
+ expect(aggregated?.nested?.amount.toFloat()).to.equal(3); // last value only
760
+ expect(aggregated?.nested?.description).to.equal('second');
761
+ // Array of objects with Currency should be from last item
762
+ expect(aggregated?.fees[0]?.amount.toFloat()).to.equal(0.7);
763
+ expect(aggregated?.fees[0]?.name).to.equal('fee1');
764
+ });
765
+ });
766
+ });
767
+ //# sourceMappingURL=Processor.test.js.map