From f1b0d9c317b2f8683004ec4a4449b6668ce0d8ca Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 19 Aug 2022 12:29:23 +0200 Subject: [PATCH 1/8] feat: create loanService --- src/mappings/services/loanService.ts | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/mappings/services/loanService.ts diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts new file mode 100644 index 00000000..285fc0bb --- /dev/null +++ b/src/mappings/services/loanService.ts @@ -0,0 +1,29 @@ +import { Loan } from '../../types' + +export class LoanService { + readonly loan: Loan + + constructor(loan: Loan) { + this.loan = loan + } + + static init = async (poolId: string, loanId: number, timestamp: Date) => { + logger.info(`Initialising loan ${loanId} for pool ${poolId}`) + const loan = new Loan(`${poolId}-${loanId.toString()}`) + + loan.createdAt = timestamp + // init logic + + return new LoanService(loan) + } + + static getById = async (loanId: string) => { + const loan = await Loan.get(loanId) + if (loan === undefined) return undefined + return new LoanService(loan) + } + + save = async () => { + await this.loan.save() + } +} From 8db908cdff7f990a252aa3f3dde6b1a27c7b668c Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 19 Aug 2022 13:18:53 +0200 Subject: [PATCH 2/8] refactor: improved service getters --- src/mappings/handlers/loansHandlers.ts | 12 +++++++++++- src/mappings/handlers/poolsHandlers.ts | 14 +++++++------- src/mappings/services/epochService.ts | 16 ++++++++-------- src/mappings/services/loanService.ts | 8 ++++---- src/mappings/services/trancheService.ts | 6 +++--- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 5c3cc2b6..81e249ba 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -1,7 +1,17 @@ import { SubstrateEvent } from '@subql/types' -import { LoanBorrowedEvent } from '../../helpers/types' +import { LoanBorrowedEvent, LoanCreatedClosedEvent } from '../../helpers/types' import { errorHandler } from '../../helpers/errorHandler' import { PoolService } from '../services/poolService' +import { LoanService } from '../services/loanService' + +export const handleLoanCreated = errorHandler(_handleLoanCreated) +async function _handleLoanCreated(event: SubstrateEvent) { + const [poolId, loanId, collateral] = event.event.data + logger.info(`Loan created event for pool: ${poolId.toString()} loan: ${loanId.toString()}`) + + const loan = await LoanService.init(poolId.toString(), loanId.toString(), event.block.timestamp) + await loan.save() +} export const handleBorrowings = errorHandler(_handleBorrowings) async function _handleBorrowings(event: SubstrateEvent): Promise { diff --git a/src/mappings/handlers/poolsHandlers.ts b/src/mappings/handlers/poolsHandlers.ts index 7bde6bdd..814aa48e 100644 --- a/src/mappings/handlers/poolsHandlers.ts +++ b/src/mappings/handlers/poolsHandlers.ts @@ -59,7 +59,7 @@ async function _handleEpochClosed(event: SubstrateEvent): Promise): Promise< block ${event.block.block.header.number.toString()}` ) - const epoch = await EpochService.getById(`${poolId.toString()}-${epochId.toString()}`) + const epoch = await EpochService.getById(poolId.toString(), epochId.toNumber()) await epoch.executeEpoch(event.block.timestamp) await epoch.save() @@ -95,7 +95,7 @@ async function _handleEpochExecuted(event: SubstrateEvent): Promise< // Compute and save aggregated order fulfillment const tranches = await TrancheService.getByPoolId(poolId.toString()) - const nextEpoch = await EpochService.getById(`${poolId.toString()}-${(epochId.toNumber() + 1).toString()}`) + const nextEpoch = await EpochService.getById(poolId.toString(), epochId.toNumber() + 1) for (const tranche of tranches) { const epochState = epoch.epochStates.find((epochState) => epochState.trancheId === tranche.tranche.trancheId) await tranche.updateSupply() @@ -212,12 +212,12 @@ async function _handleInvestOrderUpdated(event: SubstrateEvent): Pro await oo.save() // Update tranche outstanding total - const tranche = await TrancheService.getById(`${poolId.toString()}-${trancheId.toHex()}`) + const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex()) await tranche.updateOutstandingInvestOrders(newAmount.toBigInt(), oldAmount.toBigInt()) await tranche.save() // Update epochState outstanding total - const epoch = await EpochService.getById(`${poolId.toString()}-${pool.pool.currentEpoch}`) + const epoch = await EpochService.getById(poolId.toString(), pool.pool.currentEpoch) await epoch.updateOutstandingInvestOrders(trancheId.toHex(), newAmount.toBigInt(), oldAmount.toBigInt()) await epoch.save() } @@ -257,12 +257,12 @@ async function _handleRedeemOrderUpdated(event: SubstrateEvent): Pro await oo.save() // Update tranche outstanding total - const tranche = await TrancheService.getById(`${poolId.toString()}-${trancheId.toHex()}`) + const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex()) await tranche.updateOutstandingRedeemOrders(newAmount.toBigInt(), oldAmount.toBigInt()) await tranche.save() // Update epochState outstanding total - const epoch = await EpochService.getById(`${poolId.toString()}-${pool.pool.currentEpoch}`) + const epoch = await EpochService.getById(poolId.toString(), pool.pool.currentEpoch) await epoch.updateOutstandingRedeemOrders(trancheId.toHex(), newAmount.toBigInt(), oldAmount.toBigInt()) await epoch.save() } diff --git a/src/mappings/services/epochService.ts b/src/mappings/services/epochService.ts index c08315f8..486e9f4f 100644 --- a/src/mappings/services/epochService.ts +++ b/src/mappings/services/epochService.ts @@ -13,11 +13,11 @@ export class EpochService { this.epochStates = epochStates } - static init = async (poolId: string, epochId: number, trancheIds: string[], timestamp: Date) => { - logger.info(`Initialising epoch ${epochId} for pool ${poolId}`) - const epoch = new Epoch(`${poolId}-${epochId.toString()}`) + static init = async (poolId: string, epochNr: number, trancheIds: string[], timestamp: Date) => { + logger.info(`Initialising epoch ${epochNr} for pool ${poolId}`) + const epoch = new Epoch(`${poolId}-${epochNr.toString()}`) - epoch.index = epochId + epoch.index = epochNr epoch.poolId = poolId epoch.openedAt = timestamp @@ -26,7 +26,7 @@ export class EpochService { const epochStates: EpochState[] = [] for (const trancheId of trancheIds) { - const epochState = new EpochState(`${poolId}-${epochId}-${trancheId}`) + const epochState = new EpochState(`${poolId}-${epochNr}-${trancheId}`) epochState.epochId = epoch.id epochState.trancheId = trancheId epochState.outstandingInvestOrders = BigInt(0) @@ -36,10 +36,10 @@ export class EpochService { return new EpochService(epoch, epochStates) } - static getById = async (epochId: string) => { - const epoch = await Epoch.get(epochId) + static getById = async (poolId: string, epochNr: number) => { + const epoch = await Epoch.get(`${poolId}-${epochNr.toString()}`) if (epoch === undefined) return undefined - const epochStates = await EpochState.getByEpochId(epochId) + const epochStates = await EpochState.getByEpochId(`${poolId}-${epochNr.toString()}`) return new EpochService(epoch, epochStates) } diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index 285fc0bb..2d728ab4 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -7,9 +7,9 @@ export class LoanService { this.loan = loan } - static init = async (poolId: string, loanId: number, timestamp: Date) => { + static init = async (poolId: string, loanId: string, timestamp: Date) => { logger.info(`Initialising loan ${loanId} for pool ${poolId}`) - const loan = new Loan(`${poolId}-${loanId.toString()}`) + const loan = new Loan(`${poolId}-${loanId}`) loan.createdAt = timestamp // init logic @@ -17,8 +17,8 @@ export class LoanService { return new LoanService(loan) } - static getById = async (loanId: string) => { - const loan = await Loan.get(loanId) + static getById = async (poolId: string, loanId: string) => { + const loan = await Loan.get(`${poolId}-${loanId}`) if (loan === undefined) return undefined return new LoanService(loan) } diff --git a/src/mappings/services/trancheService.ts b/src/mappings/services/trancheService.ts index edb1f519..7c7508a5 100644 --- a/src/mappings/services/trancheService.ts +++ b/src/mappings/services/trancheService.ts @@ -41,9 +41,9 @@ export class TrancheService { return new TrancheService(tranche, trancheState) } - static getById = async (trancheId: string) => { - const tranche = await Tranche.get(trancheId) - const trancheState = await TrancheState.get(trancheId) + static getById = async (poolId: string, trancheId: string) => { + const tranche = await Tranche.get(`${poolId}-${trancheId}`) + const trancheState = await TrancheState.get(`${poolId}-${trancheId}`) if (tranche === undefined || trancheState === undefined) return undefined return new TrancheService(tranche, trancheState) } From 78cb08bda0d44870172362a7e20323e255f3c5e2 Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 19 Aug 2022 13:45:38 +0200 Subject: [PATCH 3/8] feat: increase outstandingDebt on borrowings --- project.yaml | 7 ++++++- src/mappings/handlers/loansHandlers.ts | 13 ++++++++++--- src/mappings/services/loanService.ts | 6 +++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/project.yaml b/project.yaml index 86857139..8ccf5258 100644 --- a/project.yaml +++ b/project.yaml @@ -73,7 +73,12 @@ dataSources: filter: module: proxy method: AnonymousCreated - - handler: handleBorrowings + - handler: handleLoanCreated + kind: substrate/EventHandler + filter: + module: loans + method: Created + - handler: handleLoanBorrowed kind: substrate/EventHandler filter: module: loans diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 81e249ba..09f85586 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -13,10 +13,17 @@ async function _handleLoanCreated(event: SubstrateEvent) await loan.save() } -export const handleBorrowings = errorHandler(_handleBorrowings) -async function _handleBorrowings(event: SubstrateEvent): Promise { - const [poolId, , amount] = event.event.data +export const handleLoanBorrowed = errorHandler(_handleLoanBorrowed) +async function _handleLoanBorrowed(event: SubstrateEvent): Promise { + const [poolId, loanId, amount] = event.event.data logger.info(`Pool: ${poolId.toString()} borrowed ${amount.toString()}`) + + // Update loan amount + const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + await loan.increaseOutstandingDebt(amount.toBigInt()) + await loan.save() + + // Update poolState info const poolService = await PoolService.getById(poolId.toString()) await poolService.increaseTotalBorrowings(amount.toBigInt()) await poolService.save() diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index 2d728ab4..04de5b10 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -12,7 +12,7 @@ export class LoanService { const loan = new Loan(`${poolId}-${loanId}`) loan.createdAt = timestamp - // init logic + loan.outstandingDebt = BigInt(0) return new LoanService(loan) } @@ -26,4 +26,8 @@ export class LoanService { save = async () => { await this.loan.save() } + + increaseOutstandingDebt = (amount: bigint) => { + this.loan.outstandingDebt = this.loan.outstandingDebt + amount + } } From 5acc428fcdb0256e46a162fd63a3f10e0d71c46d Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 19 Aug 2022 15:45:51 +0200 Subject: [PATCH 4/8] feat: basics for handle loan prices --- schema.graphql | 3 +++ src/helpers/types.ts | 12 +++++++++++- src/mappings/handlers/loansHandlers.ts | 12 +++++++++++- src/mappings/services/loanService.ts | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/schema.graphql b/schema.graphql index dbd1922a..026e9dbf 100644 --- a/schema.graphql +++ b/schema.graphql @@ -254,6 +254,9 @@ type Loan @entity { status: LoanStatus! # TODO: how to store loan type data + type: String + spec: String + interestRatePerSec: BigInt outstandingDebt: BigInt diff --git a/src/helpers/types.ts b/src/helpers/types.ts index b561affb..2f0ca37a 100644 --- a/src/helpers/types.ts +++ b/src/helpers/types.ts @@ -110,14 +110,24 @@ export interface AssetMetadata extends Struct { existentialDeposit: u128 } -export type LoanAsset = ITuple<[u64, u128]> +export interface LoanSpecs extends Struct { + advanceRate: u128 + value: u128 + probabilityOfDefault?: u128 + lossGivenDefault?: u128 + discountRate?: u128 + maturityDate?: u64 +} +export type LoanAsset = ITuple<[u64, u128]> export type PoolEvent = ITuple<[u64]> // poolId, loanId, collateral export type LoanCreatedClosedEvent = ITuple<[u64, u128, LoanAsset]> // poolId, loanId, amount export type LoanBorrowedEvent = ITuple<[u64, u128, u128]> +//poolId, loanId, interestRatePerSec, loanType +export type LoanPricedEvent = ITuple<[u64, u128, u128, Enum]> export type EpochEvent = ITuple<[u64, u32]> export type EpochSolutionEvent = ITuple<[u64, u32, EpochSolution]> diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 09f85586..2c30697b 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -1,5 +1,5 @@ import { SubstrateEvent } from '@subql/types' -import { LoanBorrowedEvent, LoanCreatedClosedEvent } from '../../helpers/types' +import { LoanBorrowedEvent, LoanCreatedClosedEvent, LoanPricedEvent, LoanSpecs } from '../../helpers/types' import { errorHandler } from '../../helpers/errorHandler' import { PoolService } from '../services/poolService' import { LoanService } from '../services/loanService' @@ -28,3 +28,13 @@ async function _handleLoanBorrowed(event: SubstrateEvent): Pr await poolService.increaseTotalBorrowings(amount.toBigInt()) await poolService.save() } + +export const handleLoanPriced = errorHandler(_handleLoanPriced) +async function _handleLoanPriced(event: SubstrateEvent) { + const [poolId, loanId, interestRatePerSec, loanType] = event.event.data + logger.info(`Loan priced event for pool: ${poolId.toString()} loan: ${loanId.toString()}`) + + const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + await loan.updateInterestRate(interestRatePerSec.toBigInt()) + await loan.updateLoanType(loanType.type, loanType.toHuman(false)) +} diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index 04de5b10..3b69c306 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -1,4 +1,5 @@ -import { Loan } from '../../types' +import { AnyJson } from '@polkadot/types/types' +import { Loan, LoanStatus } from '../../types' export class LoanService { readonly loan: Loan @@ -12,6 +13,7 @@ export class LoanService { const loan = new Loan(`${poolId}-${loanId}`) loan.createdAt = timestamp + loan.status = LoanStatus.CREATED loan.outstandingDebt = BigInt(0) return new LoanService(loan) @@ -28,6 +30,18 @@ export class LoanService { } increaseOutstandingDebt = (amount: bigint) => { + logger.info(`Increasing outstanding debt for loan ${this.loan.id} by ${amount}`) this.loan.outstandingDebt = this.loan.outstandingDebt + amount } + + updateInterestRate = (interestRatePerSec: bigint) => { + this.loan.interestRatePerSec = interestRatePerSec + } + + updateLoanType = (loanType: string, loanSpec: AnyJson) => { + this.loan.type = loanType + + const specBuff = Buffer.from(JSON.stringify(loanSpec)) + this.loan.spec = specBuff.toString('base64') + } } From 5b986b8a7d7a3067cdb25009e7b63fa90dc15f3d Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 26 Aug 2022 09:37:05 +0200 Subject: [PATCH 5/8] feat: save priced loans --- src/mappings/handlers/loansHandlers.ts | 3 ++- src/mappings/services/loanService.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 2c30697b..f9a9f08e 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -36,5 +36,6 @@ async function _handleLoanPriced(event: SubstrateEvent) { const loan = await LoanService.getById(poolId.toString(), loanId.toString()) await loan.updateInterestRate(interestRatePerSec.toBigInt()) - await loan.updateLoanType(loanType.type, loanType.toHuman(false)) + await loan.updateLoanType(loanType.type, loanType.inner.toJSON()) + await loan.save() } diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index 3b69c306..c9622350 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -13,6 +13,7 @@ export class LoanService { const loan = new Loan(`${poolId}-${loanId}`) loan.createdAt = timestamp + loan.poolId = poolId loan.status = LoanStatus.CREATED loan.outstandingDebt = BigInt(0) @@ -38,7 +39,7 @@ export class LoanService { this.loan.interestRatePerSec = interestRatePerSec } - updateLoanType = (loanType: string, loanSpec: AnyJson) => { + updateLoanType = (loanType: string, loanSpec?: AnyJson) => { this.loan.type = loanType const specBuff = Buffer.from(JSON.stringify(loanSpec)) From 6abd1cac2e9f1141093549c8ff74540ce81d9b60 Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 26 Aug 2022 10:39:08 +0200 Subject: [PATCH 6/8] feat: activate loan when priced --- project.yaml | 6 +++++- src/mappings/handlers/loansHandlers.ts | 8 ++++---- src/mappings/services/loanService.ts | 16 +++++++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/project.yaml b/project.yaml index 9de7d78e..53e5dec1 100644 --- a/project.yaml +++ b/project.yaml @@ -22,7 +22,6 @@ dataSources: - kind: substrate/Runtime startBlock: 1 mapping: - file: ./dist/index.js handlers: - handler: handleBlock @@ -84,6 +83,11 @@ dataSources: filter: module: loans method: Borrowed + - handler: handleLoanPriced + kind: substrate/EventHandler + filter: + module: loans + method: Priced - handler: handleTokenTransfer kind: substrate/EventHandler filter: diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index f9a9f08e..30775760 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -1,12 +1,12 @@ import { SubstrateEvent } from '@subql/types' -import { LoanBorrowedEvent, LoanCreatedClosedEvent, LoanPricedEvent, LoanSpecs } from '../../helpers/types' +import { LoanBorrowedEvent, LoanCreatedClosedEvent, LoanPricedEvent } from '../../helpers/types' import { errorHandler } from '../../helpers/errorHandler' import { PoolService } from '../services/poolService' import { LoanService } from '../services/loanService' export const handleLoanCreated = errorHandler(_handleLoanCreated) async function _handleLoanCreated(event: SubstrateEvent) { - const [poolId, loanId, collateral] = event.event.data + const [poolId, loanId] = event.event.data logger.info(`Loan created event for pool: ${poolId.toString()} loan: ${loanId.toString()}`) const loan = await LoanService.init(poolId.toString(), loanId.toString(), event.block.timestamp) @@ -16,7 +16,7 @@ async function _handleLoanCreated(event: SubstrateEvent) export const handleLoanBorrowed = errorHandler(_handleLoanBorrowed) async function _handleLoanBorrowed(event: SubstrateEvent): Promise { const [poolId, loanId, amount] = event.event.data - logger.info(`Pool: ${poolId.toString()} borrowed ${amount.toString()}`) + logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`) // Update loan amount const loan = await LoanService.getById(poolId.toString(), loanId.toString()) @@ -33,8 +33,8 @@ export const handleLoanPriced = errorHandler(_handleLoanPriced) async function _handleLoanPriced(event: SubstrateEvent) { const [poolId, loanId, interestRatePerSec, loanType] = event.event.data logger.info(`Loan priced event for pool: ${poolId.toString()} loan: ${loanId.toString()}`) - const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + await loan.activate() await loan.updateInterestRate(interestRatePerSec.toBigInt()) await loan.updateLoanType(loanType.type, loanType.inner.toJSON()) await loan.save() diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index c9622350..c416e436 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -26,23 +26,29 @@ export class LoanService { return new LoanService(loan) } - save = async () => { + public save = async () => { await this.loan.save() } - increaseOutstandingDebt = (amount: bigint) => { + public increaseOutstandingDebt = (amount: bigint) => { logger.info(`Increasing outstanding debt for loan ${this.loan.id} by ${amount}`) this.loan.outstandingDebt = this.loan.outstandingDebt + amount } - updateInterestRate = (interestRatePerSec: bigint) => { + public updateInterestRate = (interestRatePerSec: bigint) => { + logger.info(`Updating interest rate for loan ${this.loan.id} to ${interestRatePerSec}`) this.loan.interestRatePerSec = interestRatePerSec } - updateLoanType = (loanType: string, loanSpec?: AnyJson) => { + public updateLoanType = (loanType: string, loanSpec?: AnyJson) => { + logger.info(`Updating loan type for loan ${this.loan.id} to ${loanType}`) this.loan.type = loanType - const specBuff = Buffer.from(JSON.stringify(loanSpec)) this.loan.spec = specBuff.toString('base64') } + + public activate = () => { + logger.info(`Activating loan ${this.loan.id}`) + this.loan.status = LoanStatus.ACTIVE + } } From 3e41c9909a4a46c72f2d2e71755e88490633f0f6 Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Fri, 26 Aug 2022 12:01:27 +0200 Subject: [PATCH 7/8] feat: handle writeoffs --- project.yaml | 5 +++++ src/helpers/types.ts | 4 +++- src/mappings/handlers/loansHandlers.ts | 28 ++++++++++++++++++++++++-- src/mappings/services/loanService.ts | 13 ++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/project.yaml b/project.yaml index 53e5dec1..3f719971 100644 --- a/project.yaml +++ b/project.yaml @@ -88,6 +88,11 @@ dataSources: filter: module: loans method: Priced + - handler: handleLoanRepaid + kind: substrate/EventHandler + filter: + module: loans + method: Repaid - handler: handleTokenTransfer kind: substrate/EventHandler filter: diff --git a/src/helpers/types.ts b/src/helpers/types.ts index ef4436c3..daf74a60 100644 --- a/src/helpers/types.ts +++ b/src/helpers/types.ts @@ -127,9 +127,11 @@ export type PoolEvent = ITuple<[u64]> // poolId, loanId, collateral export type LoanCreatedClosedEvent = ITuple<[u64, u128, LoanAsset]> // poolId, loanId, amount -export type LoanBorrowedEvent = ITuple<[u64, u128, u128]> +export type LoanBorrowedRepaidEvent = ITuple<[u64, u128, u128]> //poolId, loanId, interestRatePerSec, loanType export type LoanPricedEvent = ITuple<[u64, u128, u128, Enum]> +//poolId, loanId, percentage, penaltyInterestRatePerSec, writeOffGroupIndex +export type LoanWrittenOffEvent = ITuple<[u64, u128, u128, u128, Option]> export type EpochEvent = ITuple<[u64, u32]> export type EpochSolutionEvent = ITuple<[u64, u32, EpochSolution]> diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 30775760..778e5d78 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -1,5 +1,10 @@ import { SubstrateEvent } from '@subql/types' -import { LoanBorrowedEvent, LoanCreatedClosedEvent, LoanPricedEvent } from '../../helpers/types' +import { + LoanBorrowedRepaidEvent, + LoanCreatedClosedEvent, + LoanPricedEvent, + LoanWrittenOffEvent, +} from '../../helpers/types' import { errorHandler } from '../../helpers/errorHandler' import { PoolService } from '../services/poolService' import { LoanService } from '../services/loanService' @@ -14,7 +19,7 @@ async function _handleLoanCreated(event: SubstrateEvent) } export const handleLoanBorrowed = errorHandler(_handleLoanBorrowed) -async function _handleLoanBorrowed(event: SubstrateEvent): Promise { +async function _handleLoanBorrowed(event: SubstrateEvent): Promise { const [poolId, loanId, amount] = event.event.data logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`) @@ -39,3 +44,22 @@ async function _handleLoanPriced(event: SubstrateEvent) { await loan.updateLoanType(loanType.type, loanType.inner.toJSON()) await loan.save() } + +export const handleLoanRepaid = errorHandler(_handleLoanRepaid) +async function _handleLoanRepaid(event: SubstrateEvent) { + const [poolId, loanId, amount] = event.event.data + logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`) + const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + await loan.decreaseOutstandingDebt(amount.toBigInt()) + await loan.save() +} + +export const handleLoanWrittenOff = errorHandler(_handleLoanWrittenOff) +async function _handleLoanWrittenOff(event: SubstrateEvent) { + const [poolId, loanId, percentage, , writeOffGroupIndex] = event.event.data + logger.info(`Loan writtenoff event for pool: ${poolId.toString()} loanId: ${loanId.toString()}`) + const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + const writeOffIndex = writeOffGroupIndex.isSome ? writeOffGroupIndex.unwrap().toNumber() : null + await loan.writeOff(percentage.toBigInt(), writeOffIndex) + await loan.save() +} diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index c416e436..85dbd87b 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -1,4 +1,6 @@ import { AnyJson } from '@polkadot/types/types' +import { bnToBn, nToBigInt } from '@polkadot/util' +import { WAD } from 'centrifuge-subql/config' import { Loan, LoanStatus } from '../../types' export class LoanService { @@ -35,11 +37,22 @@ export class LoanService { this.loan.outstandingDebt = this.loan.outstandingDebt + amount } + public decreaseOutstandingDebt = (amount: bigint) => { + logger.info(`Decreasing outstanding debt for loan ${this.loan.id} by ${amount}`) + this.loan.outstandingDebt = this.loan.outstandingDebt - amount + } + public updateInterestRate = (interestRatePerSec: bigint) => { logger.info(`Updating interest rate for loan ${this.loan.id} to ${interestRatePerSec}`) this.loan.interestRatePerSec = interestRatePerSec } + public writeOff = (percentage: bigint, writeOffIndex: number) => { + logger.info(`Writing off loan ${this.loan.id} with ${percentage}`) + this.loan.outstandingDebt = nToBigInt(bnToBn(this.loan.outstandingDebt).mul(bnToBn(percentage).div(WAD))) + this.loan.writeOffIndex = writeOffIndex + } + public updateLoanType = (loanType: string, loanSpec?: AnyJson) => { logger.info(`Updating loan type for loan ${this.loan.id} to ${loanType}`) this.loan.type = loanType From 3b81ed8df1c95fc4e2835ffce3e8252e5e4212d1 Mon Sep 17 00:00:00 2001 From: Filippo Fontana Date: Mon, 29 Aug 2022 14:31:03 +0200 Subject: [PATCH 8/8] feat: handle loan closed and write offs --- .github/workflows/deploy.yml | 2 +- project.yaml | 5 +++++ schema.graphql | 6 +++++- src/mappings/handlers/loansHandlers.ts | 17 +++++++++++++---- src/mappings/services/loanService.ts | 22 ++++++++++++++-------- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ab07f3fd..e9703437 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -63,5 +63,5 @@ jobs: --projectName="$SUBQL_PROJ_NAME" \ --ipfsCID="$IPFSCID" \ --type=primary \ - --indexerVersion="v1.6.1" \ + --indexerVersion="v1.9.1" \ --queryVersion="v1.5.0" diff --git a/project.yaml b/project.yaml index 3f719971..c0d5553e 100644 --- a/project.yaml +++ b/project.yaml @@ -93,6 +93,11 @@ dataSources: filter: module: loans method: Repaid + - handler: handleLoanClosed + kind: substrate/EventHandler + filter: + module: loans + method: Closed - handler: handleTokenTransfer kind: substrate/EventHandler filter: diff --git a/schema.graphql b/schema.graphql index 026e9dbf..4fa11622 100644 --- a/schema.graphql +++ b/schema.graphql @@ -252,7 +252,6 @@ type Loan @entity { # collateral: status: LoanStatus! - # TODO: how to store loan type data type: String spec: String @@ -260,7 +259,12 @@ type Loan @entity { interestRatePerSec: BigInt outstandingDebt: BigInt + totalBorrowed: BigInt + totalRepaid: BigInt + writeOffIndex: Int + writtenOffPercentage: BigInt + penaltyInterestRatePerSec: BigInt adminWrittenOff: Boolean pool: Pool! diff --git a/src/mappings/handlers/loansHandlers.ts b/src/mappings/handlers/loansHandlers.ts index 778e5d78..f9f8bbe3 100644 --- a/src/mappings/handlers/loansHandlers.ts +++ b/src/mappings/handlers/loansHandlers.ts @@ -25,7 +25,7 @@ async function _handleLoanBorrowed(event: SubstrateEvent) const [poolId, loanId, amount] = event.event.data logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`) const loan = await LoanService.getById(poolId.toString(), loanId.toString()) - await loan.decreaseOutstandingDebt(amount.toBigInt()) + await loan.repay(amount.toBigInt()) await loan.save() } export const handleLoanWrittenOff = errorHandler(_handleLoanWrittenOff) async function _handleLoanWrittenOff(event: SubstrateEvent) { - const [poolId, loanId, percentage, , writeOffGroupIndex] = event.event.data + const [poolId, loanId, percentage, penaltyInterestRatePerSec, writeOffGroupIndex] = event.event.data logger.info(`Loan writtenoff event for pool: ${poolId.toString()} loanId: ${loanId.toString()}`) const loan = await LoanService.getById(poolId.toString(), loanId.toString()) const writeOffIndex = writeOffGroupIndex.isSome ? writeOffGroupIndex.unwrap().toNumber() : null - await loan.writeOff(percentage.toBigInt(), writeOffIndex) + await loan.writeOff(percentage.toBigInt(), penaltyInterestRatePerSec.toBigInt(), writeOffIndex) + await loan.save() +} + +export const handleLoanClosed = errorHandler(_handleLoanClosed) +async function _handleLoanClosed(event: SubstrateEvent) { + const [poolId, loanId] = event.event.data + logger.info(`Loan closed event for pool: ${poolId.toString()} loanId: ${loanId.toString()}`) + const loan = await LoanService.getById(poolId.toString(), loanId.toString()) + await loan.close() await loan.save() } diff --git a/src/mappings/services/loanService.ts b/src/mappings/services/loanService.ts index 85dbd87b..670dc06f 100644 --- a/src/mappings/services/loanService.ts +++ b/src/mappings/services/loanService.ts @@ -1,6 +1,4 @@ import { AnyJson } from '@polkadot/types/types' -import { bnToBn, nToBigInt } from '@polkadot/util' -import { WAD } from 'centrifuge-subql/config' import { Loan, LoanStatus } from '../../types' export class LoanService { @@ -18,6 +16,8 @@ export class LoanService { loan.poolId = poolId loan.status = LoanStatus.CREATED loan.outstandingDebt = BigInt(0) + loan.totalBorrowed = BigInt(0) + loan.totalRepaid = BigInt(0) return new LoanService(loan) } @@ -32,14 +32,14 @@ export class LoanService { await this.loan.save() } - public increaseOutstandingDebt = (amount: bigint) => { + public borrow = (amount: bigint) => { logger.info(`Increasing outstanding debt for loan ${this.loan.id} by ${amount}`) - this.loan.outstandingDebt = this.loan.outstandingDebt + amount + this.loan.totalBorrowed = this.loan.totalBorrowed + amount } - public decreaseOutstandingDebt = (amount: bigint) => { + public repay = (amount: bigint) => { logger.info(`Decreasing outstanding debt for loan ${this.loan.id} by ${amount}`) - this.loan.outstandingDebt = this.loan.outstandingDebt - amount + this.loan.outstandingDebt = this.loan.totalRepaid + amount } public updateInterestRate = (interestRatePerSec: bigint) => { @@ -47,9 +47,10 @@ export class LoanService { this.loan.interestRatePerSec = interestRatePerSec } - public writeOff = (percentage: bigint, writeOffIndex: number) => { + public writeOff = (percentage: bigint, penaltyInterestRatePerSec: bigint, writeOffIndex: number) => { logger.info(`Writing off loan ${this.loan.id} with ${percentage}`) - this.loan.outstandingDebt = nToBigInt(bnToBn(this.loan.outstandingDebt).mul(bnToBn(percentage).div(WAD))) + this.loan.writtenOffPercentage = percentage + this.loan.penaltyInterestRatePerSec = penaltyInterestRatePerSec this.loan.writeOffIndex = writeOffIndex } @@ -64,4 +65,9 @@ export class LoanService { logger.info(`Activating loan ${this.loan.id}`) this.loan.status = LoanStatus.ACTIVE } + + public close = () => { + logger.info(`Closing loan ${this.loan.id}`) + this.loan.status = LoanStatus.CLOSED + } }