Skip to content
This repository has been archived by the owner on May 7, 2024. It is now read-only.

74 index loanentity data types #86

Merged
merged 9 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
23 changes: 21 additions & 2 deletions project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ dataSources:
- kind: substrate/Runtime
startBlock: 1
mapping:

file: ./dist/index.js
handlers:
- handler: handleBlock
Expand Down Expand Up @@ -74,11 +73,31 @@ 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
method: Borrowed
- handler: handleLoanPriced
kind: substrate/EventHandler
filter:
module: loans
method: Priced
- handler: handleLoanRepaid
kind: substrate/EventHandler
filter:
module: loans
method: Repaid
- handler: handleLoanClosed
kind: substrate/EventHandler
filter:
module: loans
method: Closed
- handler: handleTokenTransfer
kind: substrate/EventHandler
filter:
Expand Down
9 changes: 8 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,19 @@ type Loan @entity {

# collateral:
status: LoanStatus!
# TODO: how to store loan type data

type: String
spec: String

interestRatePerSec: BigInt
outstandingDebt: BigInt

totalBorrowed: BigInt
totalRepaid: BigInt

writeOffIndex: Int
writtenOffPercentage: BigInt
penaltyInterestRatePerSec: BigInt
adminWrittenOff: Boolean

pool: Pool!
Expand Down
16 changes: 14 additions & 2 deletions src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,26 @@ 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]>
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<u32>]>

export type EpochEvent = ITuple<[u64, u32]>
export type EpochSolutionEvent = ITuple<[u64, u32, EpochSolution]>
Expand Down
71 changes: 66 additions & 5 deletions src/mappings/handlers/loansHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,74 @@
import { SubstrateEvent } from '@subql/types'
import { LoanBorrowedEvent } 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'

export const handleBorrowings = errorHandler(_handleBorrowings)
async function _handleBorrowings(event: SubstrateEvent<LoanBorrowedEvent>): Promise<void> {
const [poolId, , amount] = event.event.data
logger.info(`Pool: ${poolId.toString()} borrowed ${amount.toString()}`)
export const handleLoanCreated = errorHandler(_handleLoanCreated)
async function _handleLoanCreated(event: SubstrateEvent<LoanCreatedClosedEvent>) {
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)
await loan.save()
}

export const handleLoanBorrowed = errorHandler(_handleLoanBorrowed)
async function _handleLoanBorrowed(event: SubstrateEvent<LoanBorrowedRepaidEvent>): Promise<void> {
const [poolId, loanId, amount] = event.event.data
logger.info(`Loan borrowed event for pool: ${poolId.toString()} amount: ${amount.toString()}`)

// Update loan amount
const loan = await LoanService.getById(poolId.toString(), loanId.toString())
await loan.borrow(amount.toBigInt())
await loan.save()

// Update poolState info
const poolService = await PoolService.getById(poolId.toString())
await poolService.increaseTotalBorrowings(amount.toBigInt())
await poolService.save()
}

export const handleLoanPriced = errorHandler(_handleLoanPriced)
async function _handleLoanPriced(event: SubstrateEvent<LoanPricedEvent>) {
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()
}

export const handleLoanRepaid = errorHandler(_handleLoanRepaid)
async function _handleLoanRepaid(event: SubstrateEvent<LoanBorrowedRepaidEvent>) {
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.repay(amount.toBigInt())
await loan.save()
}

export const handleLoanWrittenOff = errorHandler(_handleLoanWrittenOff)
async function _handleLoanWrittenOff(event: SubstrateEvent<LoanWrittenOffEvent>) {
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(), penaltyInterestRatePerSec.toBigInt(), writeOffIndex)
await loan.save()
}

export const handleLoanClosed = errorHandler(_handleLoanClosed)
async function _handleLoanClosed(event: SubstrateEvent<LoanCreatedClosedEvent>) {
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()
}
14 changes: 7 additions & 7 deletions src/mappings/handlers/poolsHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async function _handleEpochClosed(event: SubstrateEvent<EpochEvent>): Promise<vo
)
// Close the current epoch and open a new one
const tranches = await TrancheService.getByPoolId(poolId.toString())
const epoch = await EpochService.getById(`${poolId.toString()}-${epochId.toString()}`)
const epoch = await EpochService.getById(poolId.toString(), epochId.toNumber())
await epoch.closeEpoch(event.block.timestamp)
await epoch.save()

Expand All @@ -85,7 +85,7 @@ async function _handleEpochExecuted(event: SubstrateEvent<EpochEvent>): 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()

Expand All @@ -95,7 +95,7 @@ async function _handleEpochExecuted(event: SubstrateEvent<EpochEvent>): 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()
Expand Down Expand Up @@ -212,12 +212,12 @@ async function _handleInvestOrderUpdated(event: SubstrateEvent<OrderEvent>): 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()
}
Expand Down Expand Up @@ -257,12 +257,12 @@ async function _handleRedeemOrderUpdated(event: SubstrateEvent<OrderEvent>): 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()
}
Expand Down
16 changes: 8 additions & 8 deletions src/mappings/services/epochService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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)
}

Expand Down
73 changes: 73 additions & 0 deletions src/mappings/services/loanService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { AnyJson } from '@polkadot/types/types'
import { Loan, LoanStatus } from '../../types'

export class LoanService {
readonly loan: Loan

constructor(loan: Loan) {
this.loan = loan
}

static init = async (poolId: string, loanId: string, timestamp: Date) => {
logger.info(`Initialising loan ${loanId} for pool ${poolId}`)
const loan = new Loan(`${poolId}-${loanId}`)

loan.createdAt = timestamp
loan.poolId = poolId
loan.status = LoanStatus.CREATED
loan.outstandingDebt = BigInt(0)
loan.totalBorrowed = BigInt(0)
loan.totalRepaid = BigInt(0)

return new LoanService(loan)
}

static getById = async (poolId: string, loanId: string) => {
const loan = await Loan.get(`${poolId}-${loanId}`)
if (loan === undefined) return undefined
return new LoanService(loan)
}

public save = async () => {
await this.loan.save()
}

public borrow = (amount: bigint) => {
logger.info(`Increasing outstanding debt for loan ${this.loan.id} by ${amount}`)
this.loan.totalBorrowed = this.loan.totalBorrowed + amount
}

public repay = (amount: bigint) => {
logger.info(`Decreasing outstanding debt for loan ${this.loan.id} by ${amount}`)
this.loan.outstandingDebt = this.loan.totalRepaid + 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, penaltyInterestRatePerSec: bigint, writeOffIndex: number) => {
logger.info(`Writing off loan ${this.loan.id} with ${percentage}`)
this.loan.writtenOffPercentage = percentage
this.loan.penaltyInterestRatePerSec = penaltyInterestRatePerSec
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
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
}

public close = () => {
logger.info(`Closing loan ${this.loan.id}`)
this.loan.status = LoanStatus.CLOSED
}
}
6 changes: 3 additions & 3 deletions src/mappings/services/trancheService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down