Skip to content

Commit

Permalink
feat: transfer debt handler (#82)
Browse files Browse the repository at this point in the history
* feat: transfer debt handler

* fix: remove evm account service import
  • Loading branch information
hieronx authored Dec 9, 2023
1 parent f420de0 commit c2e8acc
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 63 deletions.
5 changes: 5 additions & 0 deletions chains/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/helpers/errorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export function errorHandler<TFunc extends (...args: any[]) => any>(
export function errorLogger<E extends Error>(err: E) {
logger.error(err)
}

export const missingPool = new Error('Pool not found or untracked! Maybe it was created before starting block?')
79 changes: 36 additions & 43 deletions src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>, InvestCollection, Enum]>

// investmentId, who, processedOrders, collection, outcome(FullyCollected OR PartiallyCollected )
export type RedeemOrdersCollectedEvent = ITuple<[TrancheCurrency, AccountId32, Vec<u64>, 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<u64>,
collection: InvestCollection,
outcome: Enum
]
>
export type RedeemOrdersCollectedEvent = ITuple<
[investmentId: TrancheCurrency, who: AccountId32, collections: Vec<u64>, 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: {
Expand Down
10 changes: 5 additions & 5 deletions src/mappings/handlers/investmentsHandlers.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -19,7 +19,7 @@ async function _handleInvestOrderUpdated(event: SubstrateEvent<OrderUpdatedEvent
)

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(address.toString())
const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex())
Expand Down Expand Up @@ -80,7 +80,7 @@ async function _handleRedeemOrderUpdated(event: SubstrateEvent<OrderUpdatedEvent
)
// Get corresponding pool
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(address.toString())
const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex())
Expand Down Expand Up @@ -140,7 +140,7 @@ async function _handleInvestOrdersCollected(event: SubstrateEvent<InvestOrdersCo
)

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw new Error('Pool not found!')
if (pool === undefined) throw missingPool
const endEpochId = pool.lastEpochClosed
logger.info(`Collection for ending epoch: ${endEpochId}`)

Expand Down Expand Up @@ -185,7 +185,7 @@ async function _handleRedeemOrdersCollected(event: SubstrateEvent<RedeemOrdersCo
)

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw new Error('Pool not found!')
if (pool === undefined) throw missingPool
const endEpochId = pool.lastEpochClosed
logger.info(`Collection for ending epoch: ${endEpochId}`)

Expand Down
56 changes: 49 additions & 7 deletions src/mappings/handlers/loansHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import {
LoanBorrowedEvent,
LoanClosedEvent,
LoanCreatedEvent,
LoanDebtTransferred,
LoanRepaidEvent,
LoanWrittenOffEvent,
} from '../../helpers/types'
import { errorHandler } from '../../helpers/errorHandler'
import { errorHandler, missingPool } from '../../helpers/errorHandler'
import { PoolService } from '../services/poolService'
import { LoanService } from '../services/loanService'
import { BorrowerTransactionService } from '../services/borrowerTransactionService'
import { BorrowerTransactionData, BorrowerTransactionService } from '../services/borrowerTransactionService'
import { AccountService } from '../services/accountService'
import { EpochService } from '../services/epochService'
import { WAD } from '../../config'
Expand All @@ -20,7 +21,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 new Error('Pool not found!')
if (pool === undefined) throw missingPool

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

Expand Down Expand Up @@ -81,7 +82,7 @@ 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 new Error('Pool not found!')
if (pool === undefined) throw missingPool

const amount = borrowAmount.isInternal
? borrowAmount.asInternal.toString()
Expand Down Expand Up @@ -127,7 +128,7 @@ 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 new Error('Pool not found!')
if (pool === undefined) throw missingPool

const principalAmount = principal.isInternal
? principal.asInternal
Expand Down Expand Up @@ -181,7 +182,7 @@ async function _handleLoanWrittenOff(event: SubstrateEvent<LoanWrittenOffEvent>)
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()
Expand All @@ -193,7 +194,7 @@ async function _handleLoanClosed(event: SubstrateEvent<LoanClosedEvent>) {
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())

Expand All @@ -212,3 +213,44 @@ async function _handleLoanClosed(event: SubstrateEvent<LoanClosedEvent>) {
})
await bt.save()
}

export const handleLoanDebtTransferred = errorHandler(_handleLoanDebtTransferred)
async function _handleLoanDebtTransferred(event: SubstrateEvent<LoanDebtTransferred>) {
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<BorrowerTransactionData, 'loanId'> = {
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()
}
4 changes: 2 additions & 2 deletions src/mappings/handlers/ormlTokensHandlers.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -35,7 +35,7 @@ async function _handleTokenTransfer(event: SubstrateEvent<TokensTransferEvent>):

// 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!')
Expand Down
12 changes: 6 additions & 6 deletions src/mappings/handlers/poolsHandlers.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -57,7 +57,7 @@ async function _handlePoolUpdated(event: SubstrateEvent<PoolUpdatedEvent>): 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()
Expand Down Expand Up @@ -87,6 +87,9 @@ async function _handleEpochClosed(event: SubstrateEvent<EpochClosedExecutedEvent
logger.info(
`Epoch ${epochId.toNumber()} closed for pool ${poolId.toString()} in block ${event.block.block.header.number}`
)
const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw missingPool

// Close the current epoch and open a new one
const tranches = await TrancheService.getActives(poolId.toString())
const epoch = await EpochService.getById(poolId.toString(), epochId.toNumber())
Expand All @@ -102,9 +105,6 @@ async function _handleEpochClosed(event: SubstrateEvent<EpochClosedExecutedEvent
)
await nextEpoch.saveWithStates()

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw new Error('Pool not found!')

await pool.closeEpoch(epochId.toNumber())
await pool.save()
}
Expand All @@ -118,7 +118,7 @@ async function _handleEpochExecuted(event: SubstrateEvent<EpochClosedExecutedEve
)

const pool = await PoolService.getById(poolId.toString())
if (pool === undefined) throw new Error('Pool not found!')
if (pool === undefined) throw missingPool

const epoch = await EpochService.getById(poolId.toString(), epochId.toNumber())

Expand Down

0 comments on commit c2e8acc

Please sign in to comment.