Skip to content

Commit ebaf007

Browse files
Add fee transactions report
1 parent c26a47f commit ebaf007

File tree

7 files changed

+192
-29
lines changed

7 files changed

+192
-29
lines changed

src/Reports/Processor.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect } from 'chai'
22
import { processor } from './Processor.js'
33
import { mockPoolSnapshots } from '../tests/mocks/mockPoolSnapshots.js'
4+
import { mockFeeTransactions } from '../tests/mocks/mockPoolFeeTransactions.js'
45
import { mockTrancheSnapshots } from '../tests/mocks/mockTrancheSnapshots.js'
56
import { mockPoolFeeSnapshots } from '../tests/mocks/mockPoolFeeSnapshot.js'
67
import { mockPoolMetadata } from '../tests/mocks/mockPoolMetadata.js'
@@ -510,6 +511,19 @@ describe('Processor', () => {
510511
}
511512
})
512513
})
514+
515+
describe('fee transactions processor', () => {
516+
it('should return empty array when no transactions found', () => {
517+
expect(processor.feeTransactions({ poolFeeTransactions: [] })).to.deep.equal([])
518+
})
519+
it('should process fee transactions correctly', () => {
520+
const result = processor.feeTransactions({ poolFeeTransactions: mockFeeTransactions })
521+
expect(result).to.have.lengthOf(2)
522+
})
523+
// TODO: add tests for filtering by transaction type
524+
// find mapping for transaction type
525+
})
526+
513527
describe('applyGrouping', () => {
514528
const applyGrouping = processor['applyGrouping']
515529
const mockData = [

src/Reports/Processor.ts

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
AssetTransactionReport,
1515
AssetTransactionsData,
1616
AssetTransactionReportFilter,
17+
FeeTransactionsData,
18+
FeeTransactionReportFilter,
19+
FeeTransactionReport,
1720
} from './types.js'
1821

1922
export class Processor {
@@ -212,41 +215,57 @@ export class Processor {
212215

213216
assetTransactions(data: AssetTransactionsData, filter?: AssetTransactionReportFilter): AssetTransactionReport[] {
214217
return data.assetTransactions
215-
.filter((day) => {
218+
.filter((tx) => {
216219
if (!filter?.transactionType || filter.transactionType === 'all') {
217220
return true
218221
}
219222
if (filter.transactionType === 'created') {
220-
return day.type === 'CREATED'
223+
return tx.type === 'CREATED'
221224
}
222225
if (filter.transactionType === 'financed') {
223-
return day.type === 'BORROWED'
226+
return tx.type === 'BORROWED'
224227
}
225228
if (filter.transactionType === 'repaid') {
226-
return day.type === 'REPAID'
229+
return tx.type === 'REPAID'
227230
}
228231
if (filter.transactionType === 'priced') {
229-
return day.type === 'PRICED'
232+
return tx.type === 'PRICED'
230233
}
231234
if (filter.transactionType === 'closed') {
232-
return day.type === 'CLOSED'
235+
return tx.type === 'CLOSED'
233236
}
234237
if (filter.transactionType === 'cashTransfer') {
235-
return day.type === 'CASH_TRANSFER'
238+
return tx.type === 'CASH_TRANSFER'
236239
}
237240
return true
238241
})
239-
.filter((day) => {
240-
return !filter?.assetId || filter.assetId === day.asset.id.split('-')[1]
242+
.filter((tx) => {
243+
return !filter?.assetId || filter.assetId === tx.asset.id.split('-')[1]
241244
})
242-
.map((day) => ({
245+
.map((tx) => ({
243246
type: 'assetTransactions',
244-
timestamp: day.timestamp.toISOString(),
245-
assetId: day.asset.id,
246-
epoch: day.epochId,
247-
transactionType: day.type,
248-
amount: day.amount,
249-
transactionHash: day.hash,
247+
timestamp: tx.timestamp.toISOString(),
248+
assetId: tx.asset.id,
249+
epoch: tx.epochId,
250+
transactionType: tx.type,
251+
amount: tx.amount,
252+
transactionHash: tx.hash,
253+
}))
254+
}
255+
256+
feeTransactions(data: FeeTransactionsData, filter?: FeeTransactionReportFilter): FeeTransactionReport[] {
257+
return data.poolFeeTransactions
258+
.filter((tx) => {
259+
if (!filter?.transactionType || filter.transactionType === 'all') {
260+
return true
261+
}
262+
return filter.transactionType === tx.type
263+
})
264+
.map((tx) => ({
265+
type: 'feeTransactions',
266+
timestamp: tx.timestamp,
267+
feeId: tx.feeId,
268+
amount: tx.amount,
250269
}))
251270
}
252271

src/Reports/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import {
66
trancheSnapshotsPostProcess,
77
trancheSnapshotsQuery,
88
} from '../queries/trancheSnapshots.js'
9+
import {
10+
poolFeeTransactionQuery,
11+
poolFeeTransactionPostProcess,
12+
PoolFeeTransactionFilter,
13+
} from '../queries/poolFeeTransactions.js'
914
import { combineLatest } from 'rxjs'
1015
import { processor } from './Processor.js'
1116

@@ -116,6 +121,10 @@ export class Reports extends Entity {
116121
...dateFilter,
117122
poolId: { equalTo: this.pool.id },
118123
})
124+
const poolFeeTransactions$ = this.poolFeeTransactionsQuery({
125+
...dateFilter,
126+
poolFee: { poolId: { equalTo: this.pool.id } },
127+
})
119128

120129
switch (type) {
121130
case 'balanceSheet':
@@ -147,6 +156,10 @@ export class Reports extends Entity {
147156
return combineLatest([assetTransactions$, metadata$]).pipe(
148157
map(([assetTransactions]) => processor.assetTransactions({ assetTransactions }, filter) as T[])
149158
)
159+
case 'feeTransactions':
160+
return combineLatest([poolFeeTransactions$]).pipe(
161+
map(([poolFeeTransactions]) => processor.feeTransactions({ poolFeeTransactions }, filter) as T[])
162+
)
150163
default:
151164
throw new Error(`Unsupported report type: ${type}`)
152165
}
@@ -176,4 +189,8 @@ export class Reports extends Entity {
176189
assetTransactionsQuery(filter?: AssetTransactionFilter) {
177190
return this._root._queryIndexer(assetTransactionsQuery, { filter }, assetTransactionsPostProcess)
178191
}
192+
193+
poolFeeTransactionsQuery(filter?: PoolFeeTransactionFilter) {
194+
return this._root._queryIndexer(poolFeeTransactionQuery, { filter }, poolFeeTransactionPostProcess)
195+
}
179196
}

src/Reports/types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { AssetTransaction, AssetTransactionType } from '../queries/assetTransactions.js'
22
import { InvestorTransaction, SubqueryInvestorTransactionType } from '../queries/investorTransactions.js'
33
import { PoolFeeSnapshotsByDate } from '../queries/poolFeeSnapshots.js'
4+
import { PoolFeeTransaction } from '../queries/poolFeeTransactions.js'
45
import { PoolSnapshot } from '../queries/poolSnapshots.js'
56
import { TrancheSnapshotsByDate } from '../queries/trancheSnapshots.js'
67
import { PoolMetadata } from '../types/poolMetadata.js'
@@ -172,3 +173,23 @@ export type AssetTransactionReportFilter = {
172173
assetId?: string
173174
transactionType?: 'created' | 'financed' | 'repaid' | 'priced' | 'closed' | 'cashTransfer' | 'all'
174175
}
176+
177+
/**
178+
* Fee transactions types
179+
*/
180+
export type FeeTransactionsData = {
181+
poolFeeTransactions: PoolFeeTransaction[]
182+
}
183+
184+
export type FeeTransactionReport = {
185+
type: 'feeTransactions'
186+
timestamp: string
187+
feeId: string
188+
amount: Currency
189+
}
190+
191+
export type FeeTransactionReportFilter = {
192+
from?: string
193+
to?: string
194+
transactionType?: 'directChargeMade' | 'directChargeCanceled' | 'accrued' | 'paid' | 'all'
195+
}

src/queries/assetTransactions.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,22 @@ type SubqueryAssetTransactions = {
105105

106106
export const assetTransactionsPostProcess = (data: SubqueryAssetTransactions): AssetTransaction[] => {
107107
return (
108-
data.assetTransactions.nodes.map((day) => {
109-
const decimals = day.pool.currency.decimals
108+
data.assetTransactions.nodes.map((tx) => {
109+
const decimals = tx.pool.currency.decimals
110110
return {
111-
...day,
112-
settlementPrice: day.settlementPrice ? new Price(day.settlementPrice) : null,
113-
amount: new Currency(day?.amount ?? 0n, decimals),
114-
principalAmount: day.principalAmount ? new Currency(day.principalAmount, decimals) : undefined,
115-
interestAmount: day.interestAmount ? new Currency(day.interestAmount, decimals) : undefined,
116-
realizedProfitFifo: day.realizedProfitFifo ? new Currency(day.realizedProfitFifo, decimals) : undefined,
117-
sumRealizedProfitFifo: day.asset.sumRealizedProfitFifo
118-
? new Currency(day.asset.sumRealizedProfitFifo, decimals)
111+
...tx,
112+
settlementPrice: tx.settlementPrice ? new Price(tx.settlementPrice) : null,
113+
amount: new Currency(tx?.amount ?? 0n, decimals),
114+
principalAmount: tx.principalAmount ? new Currency(tx.principalAmount, decimals) : undefined,
115+
interestAmount: tx.interestAmount ? new Currency(tx.interestAmount, decimals) : undefined,
116+
realizedProfitFifo: tx.realizedProfitFifo ? new Currency(tx.realizedProfitFifo, decimals) : undefined,
117+
sumRealizedProfitFifo: tx.asset.sumRealizedProfitFifo
118+
? new Currency(tx.asset.sumRealizedProfitFifo, decimals)
119119
: undefined,
120-
unrealizedProfitAtMarketPrice: day.asset.unrealizedProfitAtMarketPrice
121-
? new Currency(day.asset.unrealizedProfitAtMarketPrice, decimals)
120+
unrealizedProfitAtMarketPrice: tx.asset.unrealizedProfitAtMarketPrice
121+
? new Currency(tx.asset.unrealizedProfitAtMarketPrice, decimals)
122122
: undefined,
123-
timestamp: new Date(`${day.timestamp}+00:00`),
123+
timestamp: new Date(`${tx.timestamp}+00:00`),
124124
}
125125
}) || ([] satisfies AssetTransaction[])
126126
)

src/queries/poolFeeTransactions.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { Currency } from '../utils/BigInt.js'
2+
3+
export type PoolFeeTransactionFilter = Partial<
4+
Record<keyof SubqueryPoolFeeTransaction['poolFeeTransactions']['nodes'][0], any>
5+
>
6+
7+
export type PoolFeeTransaction = {
8+
feeId: string
9+
type: string
10+
timestamp: string
11+
blockNumber: string
12+
epochNumber: number
13+
amount: Currency
14+
}
15+
16+
export type SubqueryPoolFeeTransaction = {
17+
poolFeeTransactions: {
18+
nodes: {
19+
id: string
20+
type: string
21+
timestamp: string
22+
blockNumber: string
23+
epochNumber: number
24+
amount: string
25+
poolFee: {
26+
feeId: string
27+
pool: {
28+
currency: {
29+
decimals: number
30+
}
31+
}
32+
}
33+
}[]
34+
}
35+
}
36+
37+
export function poolFeeTransactionPostProcess(data: SubqueryPoolFeeTransaction): PoolFeeTransaction[] {
38+
return data.poolFeeTransactions.nodes.map((tx) => ({
39+
feeId: tx.id,
40+
type: tx.type,
41+
timestamp: tx.timestamp,
42+
blockNumber: tx.blockNumber,
43+
epochNumber: tx.epochNumber,
44+
amount: new Currency(tx.amount, tx.poolFee.pool.currency.decimals),
45+
}))
46+
}
47+
48+
export const poolFeeTransactionQuery = `
49+
query($filter: PoolFeeTransactionFilter) {
50+
poolFeeTransactions(
51+
orderBy: TIMESTAMP_ASC,
52+
filter: $filter
53+
) {
54+
nodes {
55+
id
56+
type
57+
timestamp
58+
blockNumber
59+
epochNumber
60+
amount
61+
poolFee {
62+
feeId
63+
pool {
64+
currency {
65+
decimals
66+
}
67+
}
68+
}
69+
}
70+
}
71+
}
72+
`
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Currency } from '../../utils/BigInt.js'
2+
3+
export const mockFeeTransactions = [
4+
{
5+
feeId: 'fee-1',
6+
type: 'ACCRUED',
7+
timestamp: '2024-01-01T00:00:00Z',
8+
blockNumber: '1',
9+
epochNumber: 1,
10+
amount: new Currency(1000000n, 6),
11+
},
12+
{
13+
feeId: 'fee-2',
14+
type: 'PAID',
15+
timestamp: '2024-01-02T00:00:00Z',
16+
blockNumber: '2',
17+
epochNumber: 2,
18+
amount: new Currency(2000000n, 6),
19+
},
20+
]

0 commit comments

Comments
 (0)