Skip to content

Commit

Permalink
feat: track loan KPIs in Pool and PoolsnapshotsFixes #188
Browse files Browse the repository at this point in the history
* feat: track loan KPIs in Pool and Poolsnapshots
Fixes #188
  • Loading branch information
filo87 authored Feb 5, 2024
1 parent 4e4b3d5 commit ae9ff16
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 48 deletions.
16 changes: 16 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ type Pool @entity {
# Aggregated transaction data over the last period
sumBorrowedAmountByPeriod: BigInt
sumRepaidAmountByPeriod: BigInt
sumPrincipalRepaidAmountByPeriod: BigInt
sumInterestRepaidAmountByPeriod: BigInt
sumUnscheduledRepaidAmountByPeriod: BigInt

sumInvestedAmountByPeriod: BigInt
sumRedeemedAmountByPeriod: BigInt
sumNumberOfLoansByPeriod: BigInt
Expand All @@ -47,6 +51,9 @@ type Pool @entity {
# Cumulated transaction data since pool creation
sumBorrowedAmount: BigInt
sumRepaidAmount: BigInt
sumPrincipalRepaidAmount: BigInt
sumInterestRepaidAmount: BigInt
sumUnscheduledRepaidAmount: BigInt
sumNumberOfLoans: BigInt

tranches: [Tranche] @derivedFrom(field: "pool")
Expand All @@ -72,15 +79,24 @@ type PoolSnapshot @entity {
# Aggregated transaction data over the last period
sumBorrowedAmountByPeriod: BigInt
sumRepaidAmountByPeriod: BigInt
sumPrincipalRepaidAmountByPeriod: BigInt
sumInterestRepaidAmountByPeriod: BigInt
sumUnscheduledRepaidAmountByPeriod: BigInt

sumInvestedAmountByPeriod: BigInt
sumRedeemedAmountByPeriod: BigInt
sumNumberOfLoansByPeriod: BigInt

sumNumberOfActiveLoans: BigInt
sumDebtOverdue: BigInt
sumDebtWrittenOffByPeriod: BigInt

# Cumulated transaction data since pool creation
sumBorrowedAmount: BigInt
sumRepaidAmount: BigInt
sumPrincipalRepaidAmount: BigInt
sumInterestRepaidAmount: BigInt
sumUnscheduledRepaidAmount: BigInt
sumNumberOfLoans: BigInt
}

Expand Down
58 changes: 29 additions & 29 deletions src/mappings/handlers/loansHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SubstrateEvent } from '@subql/types'
import { nToBigInt } from '@polkadot/util'
import {
LoanBorrowedEvent,
LoanClosedEvent,
Expand All @@ -21,7 +22,7 @@ async function _handleLoanCreated(event: SubstrateEvent<LoanCreatedEvent>) {
logger.info(`Loan created event for pool: ${poolId.toString()} loan: ${loanId.toString()}`)

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
if (!pool) throw missingPool

const account = await AccountService.getOrInit(event.extrinsic.extrinsic.signer.toHex())

Expand Down Expand Up @@ -82,13 +83,13 @@ async function _handleLoanBorrowed(event: SubstrateEvent<LoanBorrowedEvent>): Pr
const [poolId, loanId, borrowAmount] = event.event.data

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
if (!pool) throw missingPool

const amount = borrowAmount.isInternal
? borrowAmount.asInternal.toString()
: borrowAmount.asExternal.quantity.mul(borrowAmount.asExternal.settlementPrice).div(WAD).toString()
? borrowAmount.asInternal.toBigInt()
: nToBigInt(borrowAmount.asExternal.quantity.toBn().mul(borrowAmount.asExternal.settlementPrice.toBn()).div(WAD))

if (amount == '0') return
if (amount === BigInt(0)) return

logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`)

Expand All @@ -97,7 +98,7 @@ async function _handleLoanBorrowed(event: SubstrateEvent<LoanBorrowedEvent>): Pr
// Update loan amount
const loan = await LoanService.getById(poolId.toString(), loanId.toString())
await loan.activate()
await loan.borrow(BigInt(amount))
await loan.borrow(amount)
await loan.updateItemMetadata()
await loan.save()

Expand All @@ -108,15 +109,15 @@ async function _handleLoanBorrowed(event: SubstrateEvent<LoanBorrowedEvent>): Pr
epochNumber: pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
amount: BigInt(amount),
principalAmount: BigInt(amount),
quantity: borrowAmount.isExternal ? BigInt(borrowAmount.asExternal.quantity.toString()) : null,
settlementPrice: borrowAmount.isExternal ? BigInt(borrowAmount.asExternal.settlementPrice.toString()) : null,
amount: amount,
principalAmount: amount,
quantity: borrowAmount.isExternal ? borrowAmount.asExternal.quantity.toBigInt() : null,
settlementPrice: borrowAmount.isExternal ? borrowAmount.asExternal.settlementPrice.toBigInt() : null,
})
await bt.save()

// Update pool info
await pool.increaseBorrowings(BigInt(amount))
await pool.increaseBorrowings(amount)
await pool.save()

// Update epoch info
Expand All @@ -131,21 +132,21 @@ async function _handleLoanRepaid(event: SubstrateEvent<LoanRepaidEvent>) {
const [poolId, loanId, { principal, interest, unscheduled }] = event.event.data

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
if (!pool) throw missingPool

const principalAmount = principal.isInternal
? principal.asInternal
: principal.asExternal.quantity.mul(principal.asExternal.settlementPrice).div(WAD)
const amount = principalAmount.add(interest).add(unscheduled).toString()
? principal.asInternal.toBigInt()
: nToBigInt(principal.asExternal.quantity.toBn().mul(principal.asExternal.settlementPrice.toBn()).div(WAD))
const amount = principalAmount + interest.toBigInt() + unscheduled.toBigInt()

if (amount == '0') return
if (amount === BigInt(0)) return

logger.info(`Loan repaid event for pool: ${poolId.toString()} amount: ${amount.toString()}`)

const account = await AccountService.getOrInit(event.extrinsic.extrinsic.signer.toHex())

const loan = await LoanService.getById(poolId.toString(), loanId.toString())
await loan.repay(BigInt(amount))
await loan.repay(amount)
await loan.updateItemMetadata()
await loan.save()

Expand All @@ -156,23 +157,23 @@ async function _handleLoanRepaid(event: SubstrateEvent<LoanRepaidEvent>) {
epochNumber: pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
amount: BigInt(amount),
principalAmount: BigInt(principalAmount.toString()),
interestAmount: BigInt(interest.toString()),
unscheduledAmount: BigInt(unscheduled.toString()),
quantity: principal.isExternal ? BigInt(principal.asExternal.quantity.toString()) : null,
settlementPrice: principal.isExternal ? BigInt(principal.asExternal.settlementPrice.toString()) : null,
amount: amount,
principalAmount: principalAmount,
interestAmount: interest.toBigInt(),
unscheduledAmount: unscheduled.toBigInt(),
quantity: principal.isExternal ? principal.asExternal.quantity.toBigInt() : null,
settlementPrice: principal.isExternal ? principal.asExternal.settlementPrice.toBigInt() : null,
})
await bt.save()

// Update pool info
await pool.increaseRepayments(BigInt(amount))
await pool.increaseRepayments(principalAmount, interest.toBigInt(), unscheduled.toBigInt())
await pool.save()

// Update epoch info
const epoch = await EpochService.getById(pool.id, pool.currentEpoch)
if (epoch === undefined) throw new Error('Epoch not found!')
await epoch.increaseRepayments(BigInt(amount))
if (!epoch) throw new Error('Epoch not found!')
await epoch.increaseRepayments(amount)
await epoch.save()
}

Expand Down Expand Up @@ -224,9 +225,9 @@ async function _handleLoanDebtTransferred(event: SubstrateEvent<LoanDebtTransfer
const [poolId, fromLoanId, toLoanId, amount] = event.event.data

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool
if (!pool) throw missingPool

if (amount.toString() == '0') return
if (amount.toBigInt() === BigInt(0)) return

logger.info(
`Loan debt transferred event for pool: ${poolId.toString()}, from loan: ${fromLoanId.toString()} ` +
Expand All @@ -247,7 +248,6 @@ async function _handleLoanDebtTransferred(event: SubstrateEvent<LoanDebtTransfer

const txData: Omit<BorrowerTransactionData, 'loanId'> = {
poolId: poolId.toString(),
//loanId: loanId.toString(),
address: account.id,
epochNumber: pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
Expand Down
3 changes: 3 additions & 0 deletions src/mappings/services/loanService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export class LoanService extends Loan {
loan.totalRepaidInterest = BigInt(0)
loan.totalRepaidUnscheduled = BigInt(0)

loan.borrowedAmountByPeriod = BigInt(0)
loan.repaidAmountByPeriod = BigInt(0)

return loan
}

Expand Down
16 changes: 11 additions & 5 deletions src/mappings/services/poolService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ describe('Given a new pool, when initialised', () => {
describe('Given an existing pool,', () => {
test('when the nav is updated, then the value is fetched and set correctly', async () => {
await pool.updatePortfolioValuation()
expect(api.query.loans.portfolioValuation).toBeCalled()
expect(api.query.loans.portfolioValuation).toHaveBeenCalled()
expect(pool.portfolioValuation).toBe(BigInt(100000000000000))
})

test('when the pool state is updated, then the values are fetched and set correctly', async () => {
await pool.updateState()
expect(api.query.poolSystem.pool).toBeCalledWith(poolId)
expect(api.query.poolSystem.pool).toHaveBeenCalledWith(poolId)
expect(pool).toMatchObject({
totalReserve: BigInt(91000000000000),
availableReserve: BigInt(92000000000000),
Expand All @@ -110,10 +110,16 @@ describe('Given an existing pool,', () => {
})

test('when total repaid are registered, then values are incremented correctly', async () => {
await pool.increaseRepayments(BigInt('17500000000000000'))
await pool.increaseRepayments(BigInt('17500000000000000'), BigInt('17500000000000000'), BigInt('17500000000000000'))
expect(pool).toMatchObject({
sumRepaidAmountByPeriod: BigInt('17500000000000000'),
sumRepaidAmount: BigInt('17500000000000000'),
sumRepaidAmountByPeriod: BigInt('17500000000000000') + BigInt('17500000000000000') + BigInt('17500000000000000'),
sumRepaidAmount: BigInt('17500000000000000') + BigInt('17500000000000000') + BigInt('17500000000000000'),
sumPrincipalRepaidAmountByPeriod: BigInt('17500000000000000'),
sumPrincipalRepaidAmount: BigInt('17500000000000000'),
sumInterestRepaidAmountByPeriod: BigInt('17500000000000000'),
sumInterestRepaidAmount: BigInt('17500000000000000'),
sumUnscheduledRepaidAmountByPeriod: BigInt('17500000000000000'),
sumUnscheduledRepaidAmount: BigInt('17500000000000000'),
})
})

Expand Down
44 changes: 30 additions & 14 deletions src/mappings/services/poolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,18 @@ export class PoolService extends Pool {

this.sumBorrowedAmountByPeriod = BigInt(0)
this.sumRepaidAmountByPeriod = BigInt(0)
this.sumPrincipalRepaidAmountByPeriod = BigInt(0)
this.sumInterestRepaidAmountByPeriod = BigInt(0)
this.sumUnscheduledRepaidAmountByPeriod = BigInt(0)
this.sumInvestedAmountByPeriod = BigInt(0)
this.sumRedeemedAmountByPeriod = BigInt(0)
this.sumNumberOfLoansByPeriod = BigInt(0)

this.sumBorrowedAmount = BigInt(0)
this.sumRepaidAmount = BigInt(0)
this.sumPrincipalRepaidAmount = BigInt(0)
this.sumInterestRepaidAmount = BigInt(0)
this.sumUnscheduledRepaidAmount = BigInt(0)
this.sumNumberOfLoans = BigInt(0)

this.currencyId = currencyId
Expand Down Expand Up @@ -127,9 +133,19 @@ export class PoolService extends Pool {
this.sumBorrowedAmount += borrowedAmount
}

public increaseRepayments(repaidAmount: bigint) {
this.sumRepaidAmountByPeriod += repaidAmount
this.sumRepaidAmount += repaidAmount
public increaseRepayments(
principalRepaidAmount: bigint,
interestRepaidAmount: bigint,
unscheduledRepaidAmount: bigint
) {
this.sumRepaidAmountByPeriod += principalRepaidAmount + interestRepaidAmount + unscheduledRepaidAmount
this.sumRepaidAmount += principalRepaidAmount + interestRepaidAmount + unscheduledRepaidAmount
this.sumPrincipalRepaidAmountByPeriod += principalRepaidAmount
this.sumPrincipalRepaidAmount += principalRepaidAmount
this.sumInterestRepaidAmountByPeriod += interestRepaidAmount
this.sumInterestRepaidAmount += interestRepaidAmount
this.sumUnscheduledRepaidAmountByPeriod += unscheduledRepaidAmount
this.sumUnscheduledRepaidAmount += unscheduledRepaidAmount
}

public increaseInvestments(currencyAmount: bigint) {
Expand Down Expand Up @@ -221,19 +237,19 @@ export class PoolService extends Pool {

export interface ActiveLoanData {
[loanId: string]: {
outstandingPrincipal: bigint,
outstandingInterest: bigint,
outstandingDebt: bigint,
presentValue: bigint,
actualMaturityDate: Date,
outstandingPrincipal: bigint
outstandingInterest: bigint
outstandingDebt: bigint
presentValue: bigint
actualMaturityDate: Date
timeToMaturity: number
actualOriginationDate: Date,
writeOffPercentage: bigint,
totalBorrowed: bigint,
actualOriginationDate: Date
writeOffPercentage: bigint
totalBorrowed: bigint
totalRepaid: bigint
totalRepaidPrincipal: bigint,
totalRepaidInterest: bigint,
totalRepaidUnscheduled: bigint,
totalRepaidPrincipal: bigint
totalRepaidInterest: bigint
totalRepaidUnscheduled: bigint
}
}

Expand Down

0 comments on commit ae9ff16

Please sign in to comment.