From c2e8accf0d22ed67095dbf7461f1afb862f38202 Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Sat, 9 Dec 2023 07:20:47 +0100 Subject: [PATCH] feat: transfer debt handler (#82) * feat: transfer debt handler * fix: remove evm account service import --- chains/base.yaml | 5 ++ src/helpers/errorHandler.ts | 2 + src/helpers/types.ts | 79 +++++++++----------- src/mappings/handlers/investmentsHandlers.ts | 10 +-- src/mappings/handlers/loansHandlers.ts | 56 ++++++++++++-- src/mappings/handlers/ormlTokensHandlers.ts | 4 +- src/mappings/handlers/poolsHandlers.ts | 12 +-- 7 files changed, 105 insertions(+), 63 deletions(-) diff --git a/chains/base.yaml b/chains/base.yaml index 41ccf24a..ed9da799 100644 --- a/chains/base.yaml +++ b/chains/base.yaml @@ -99,6 +99,11 @@ dataSources: filter: module: loans method: Closed + - handler: handleLoanDebtTransferred + kind: substrate/EventHandler + filter: + module: loans + method: DebtTransferred - handler: handleTokenTransfer kind: substrate/EventHandler filter: diff --git a/src/helpers/errorHandler.ts b/src/helpers/errorHandler.ts index 4babb7d5..8f2406fa 100644 --- a/src/helpers/errorHandler.ts +++ b/src/helpers/errorHandler.ts @@ -17,3 +17,5 @@ export function errorHandler any>( export function errorLogger(err: E) { logger.error(err) } + +export const missingPool = new Error('Pool not found or untracked! Maybe it was created before starting block?') diff --git a/src/helpers/types.ts b/src/helpers/types.ts index 6030f40d..bdc69925 100644 --- a/src/helpers/types.ts +++ b/src/helpers/types.ts @@ -319,49 +319,42 @@ export interface NftItemMetadata extends Struct { isFrozen: boolean } -// collectionId, itemId -export type LoanAsset = ITuple<[u64, u128]> - -// admin, depositor, poolId, essence -export type PoolCreatedEvent = ITuple<[AccountId32, AccountId32, u64, PoolEssence]> - -// poolId, old, new -export type PoolUpdatedEvent = ITuple<[AccountId32, PoolEssence, PoolEssence]> - -// poolId, loanId, loanInfo -export type LoanCreatedEvent = ITuple<[u64, u64, LoanInfoCreated]> -// poolId, loanId, collateralInfo -export type LoanClosedEvent = ITuple<[u64, u64, LoanAsset]> -// poolId, loanId, amount -export type LoanBorrowedEvent = ITuple<[u64, u64, LoanPricingAmount]> -// poolId, loanId, amount -export type LoanRepaidEvent = ITuple<[u64, u64, LoanPricingRepaidAmount]> -//poolId, loanId, writeOffStatus -export type LoanWrittenOffEvent = ITuple<[u64, u64, LoanWriteOffStatus]> - -// poolId, epochId -export type EpochClosedExecutedEvent = ITuple<[u64, u32]> - -// poolId, epochId, solution -export type EpochSolutionEvent = ITuple<[u64, u32, EpochSolution]> - -// investmentId, who, processedOrders, collection, outcome(FullyCollected OR PartiallyCollected ) -export type InvestOrdersCollectedEvent = ITuple<[TrancheCurrency, AccountId32, Vec, InvestCollection, Enum]> - -// investmentId, who, processedOrders, collection, outcome(FullyCollected OR PartiallyCollected ) -export type RedeemOrdersCollectedEvent = ITuple<[TrancheCurrency, AccountId32, Vec, RedeemCollection, Enum]> - -// investmentId, submittedAt, who, amount -export type OrderUpdatedEvent = ITuple<[TrancheCurrency, u64, AccountId32, u128]> - -// investmentId, orderId, fulfillment -export type OrdersClearedEvent = ITuple<[TrancheCurrency, u64, OrdersFulfillment]> - -// currencyId: 'CommonTypesTokensCurrencyId'from,to,amount -export type TokensTransferEvent = ITuple<[TokensCurrencyId, AccountId32, AccountId32, u128]> - -// currencyId, who, amount -export type TokensEndowedDepositedWithdrawnEvent = ITuple<[TokensCurrencyId, AccountId32, u128]> +export type LoanAsset = ITuple<[collectionId: u64, itemId: u128]> +export type LoanCreatedEvent = ITuple<[poolId: u64, loanId: u64, loanInfo: LoanInfoCreated]> +export type LoanClosedEvent = ITuple<[poolId: u64, loanId: u64, collateralInfo: LoanAsset]> +export type LoanBorrowedEvent = ITuple<[poolId: u64, loanId: u64, amount: LoanPricingAmount]> +export type LoanRepaidEvent = ITuple<[poolId: u64, loanId: u64, amount: LoanPricingRepaidAmount]> +export type LoanWrittenOffEvent = ITuple<[poolId: u64, loanId: u64, writeOffStatus: LoanWriteOffStatus]> +export type LoanDebtTransferred = ITuple<[poolId: u64, fromLoanId: u64, toLoanId: u64, amount: u128]> + +export type PoolCreatedEvent = ITuple<[admin: AccountId32, depositor: AccountId32, poolId: u64, essence: PoolEssence]> +export type PoolUpdatedEvent = ITuple<[admin: AccountId32, old: PoolEssence, new: PoolEssence]> + +export type EpochClosedExecutedEvent = ITuple<[poolId: u64, epochId: u32]> +export type EpochSolutionEvent = ITuple<[poolId: u64, epochId: u32, solution: EpochSolution]> + +export type InvestOrdersCollectedEvent = ITuple< + [ + investmentId: TrancheCurrency, + who: AccountId32, + processedOrders: Vec, + collection: InvestCollection, + outcome: Enum + ] +> +export type RedeemOrdersCollectedEvent = ITuple< + [investmentId: TrancheCurrency, who: AccountId32, collections: Vec, collection: RedeemCollection, outcome: Enum] +> +export type OrderUpdatedEvent = ITuple< + [investmentId: TrancheCurrency, submittedAt: u64, who: AccountId32, amount: u128] +> +export type OrdersClearedEvent = ITuple<[investmentId: TrancheCurrency, orderId: u64, fulfillment: OrdersFulfillment]> +export type TokensTransferEvent = ITuple< + [currencyId: TokensCurrencyId, from: AccountId32, to: AccountId32, amount: u128] +> +export type TokensEndowedDepositedWithdrawnEvent = ITuple< + [currencyId: TokensCurrencyId, who: AccountId32, amount: u128] +> export type ExtendedRpc = typeof api.rpc & { pools: { diff --git a/src/mappings/handlers/investmentsHandlers.ts b/src/mappings/handlers/investmentsHandlers.ts index b3242c81..75d00d3a 100644 --- a/src/mappings/handlers/investmentsHandlers.ts +++ b/src/mappings/handlers/investmentsHandlers.ts @@ -1,5 +1,5 @@ import { SubstrateEvent } from '@subql/types' -import { errorHandler } from '../../helpers/errorHandler' +import { errorHandler, missingPool } from '../../helpers/errorHandler' import { EpochService } from '../services/epochService' import { PoolService } from '../services/poolService' import { TrancheService } from '../services/trancheService' @@ -19,7 +19,7 @@ async function _handleInvestOrderUpdated(event: SubstrateEvent) { logger.info(`Loan created event for pool: ${poolId.toString()} loan: ${loanId.toString()}`) const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool const account = await AccountService.getOrInit(event.extrinsic.extrinsic.signer.toString()) @@ -81,7 +82,7 @@ async function _handleLoanBorrowed(event: SubstrateEvent): Pr const [poolId, loanId, borrowAmount] = event.event.data const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool const amount = borrowAmount.isInternal ? borrowAmount.asInternal.toString() @@ -127,7 +128,7 @@ async function _handleLoanRepaid(event: SubstrateEvent) { const [poolId, loanId, { principal, interest, unscheduled }] = event.event.data const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool const principalAmount = principal.isInternal ? principal.asInternal @@ -181,7 +182,7 @@ async function _handleLoanWrittenOff(event: SubstrateEvent) await loan.save() const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool await pool.increaseWriteOff(loan.writtenOffAmountByPeriod) await pool.save() @@ -193,7 +194,7 @@ async function _handleLoanClosed(event: SubstrateEvent) { logger.info(`Loan closed event for pool: ${poolId.toString()} loanId: ${loanId.toString()}`) const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool const account = await AccountService.getOrInit(event.extrinsic.extrinsic.signer.toString()) @@ -212,3 +213,44 @@ async function _handleLoanClosed(event: SubstrateEvent) { }) await bt.save() } + +export const handleLoanDebtTransferred = errorHandler(_handleLoanDebtTransferred) +async function _handleLoanDebtTransferred(event: SubstrateEvent) { + const [poolId, fromLoanId, toLoanId, amount] = event.event.data + + const pool = await PoolService.getById(poolId.toString()) + if (pool === undefined) throw missingPool + + logger.info( + `Loan debt transferred event for pool: ${poolId.toString()}, from loan: ${fromLoanId.toString()} ` + + `to loan: ${toLoanId.toString()} amount: ${amount.toString()}` + ) + + const account = await AccountService.getOrInit(event.extrinsic.extrinsic.signer.toString()) + + const fromLoan = await LoanService.getById(poolId.toString(), fromLoanId.toString()) + await fromLoan.repay(amount.toBigInt()) + await fromLoan.updateItemMetadata() + await fromLoan.save() + + const toLoan = await LoanService.getById(poolId.toString(), toLoanId.toString()) + await toLoan.repay(amount.toBigInt()) + await toLoan.updateItemMetadata() + await toLoan.save() + + const txData: Omit = { + poolId: poolId.toString(), + //loanId: loanId.toString(), + address: account.id, + epochNumber: pool.currentEpoch, + hash: event.extrinsic.extrinsic.hash.toString(), + timestamp: event.block.timestamp, + amount: amount.toBigInt(), + } + + const repaidBt = await BorrowerTransactionService.repaid({ ...txData, loanId: fromLoanId.toString() }) + await repaidBt.save() + + const borrowedBt = await BorrowerTransactionService.borrowed({ ...txData, loanId: fromLoanId.toString() }) + await borrowedBt.save() +} diff --git a/src/mappings/handlers/ormlTokensHandlers.ts b/src/mappings/handlers/ormlTokensHandlers.ts index c51c0586..086d8776 100644 --- a/src/mappings/handlers/ormlTokensHandlers.ts +++ b/src/mappings/handlers/ormlTokensHandlers.ts @@ -1,5 +1,5 @@ import { SubstrateEvent } from '@subql/types' -import { errorHandler } from '../../helpers/errorHandler' +import { errorHandler, missingPool } from '../../helpers/errorHandler' import { TokensEndowedDepositedWithdrawnEvent, TokensTransferEvent } from '../../helpers/types' import { AccountService } from '../services/accountService' import { CurrencyBalanceService } from '../services/currencyBalance' @@ -35,7 +35,7 @@ async function _handleTokenTransfer(event: SubstrateEvent): // Get corresponding pool const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) missingPool const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex()) if (tranche === undefined) throw new Error('Tranche not found!') diff --git a/src/mappings/handlers/poolsHandlers.ts b/src/mappings/handlers/poolsHandlers.ts index 523c7895..7f4b2675 100644 --- a/src/mappings/handlers/poolsHandlers.ts +++ b/src/mappings/handlers/poolsHandlers.ts @@ -1,5 +1,5 @@ import { SubstrateEvent } from '@subql/types' -import { errorHandler } from '../../helpers/errorHandler' +import { errorHandler, missingPool } from '../../helpers/errorHandler' import { EpochService } from '../services/epochService' import { PoolService } from '../services/poolService' import { TrancheService } from '../services/trancheService' @@ -57,7 +57,7 @@ async function _handlePoolUpdated(event: SubstrateEvent): Prom logger.info(`Pool ${poolId.toString()} updated on block ${event.block.block.header.number}`) const pool = await PoolService.getById(poolId.toString()) - if (pool === undefined) throw new Error('Pool not found!') + if (pool === undefined) throw missingPool await pool.initData() await pool.save() @@ -87,6 +87,9 @@ async function _handleEpochClosed(event: SubstrateEvent