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

Commit

Permalink
Sprint 11 Release (#19)
Browse files Browse the repository at this point in the history
* 74 index loanentity data types (#86)

* feat: create loanService

* refactor: improved service getters

* feat: increase outstandingDebt on borrowings

* feat: basics for handle loan prices

* feat: save priced loans

* feat: activate loan when priced

* feat: handle writeoffs

* feat: handle loan closed and write offs

* fix: loan.repay() should increase totalRepaid

* fix: invest execution decimals and avoid empty transactions (#87)

* 82 add prices to all investortransaction types (#88)

* chore: update chain data

* feat: extend InvestorTransaction decoration and refactor

* feat: use rpc prices from trancheTokenPrices() when investor transactions are processed (#89)

* feat: improved loans and borrower transactions (#90)

* 63 convert outflows to dai (#91)

* feat: add currency value for redeem fields

* fix: currency conversion should support null prices
  • Loading branch information
filo87 authored Sep 13, 2022
1 parent eae11f0 commit 0bf23cd
Show file tree
Hide file tree
Showing 13 changed files with 683 additions and 460 deletions.
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
39 changes: 30 additions & 9 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type PoolSnapshot @entity {

type Tranche @entity {
id: ID! #poolId-trancheId
index: Int!
type: String! @index
pool: Pool! @index

Expand All @@ -106,9 +107,11 @@ type TrancheState @entity {

outstandingInvestOrders_: BigInt!
outstandingRedeemOrders_: BigInt!
outstandingRedeemOrdersCurrency_: BigInt!

fulfilledInvestOrders_: BigInt!
fulfilledRedeemOrders_: BigInt!
fulfilledRedeemOrdersCurrency_: BigInt!

yield30DaysAnnualized: BigInt
yield90DaysAnnualized: BigInt
Expand All @@ -130,9 +133,11 @@ type TrancheSnapshot @entity {

outstandingInvestOrders_: BigInt!
outstandingRedeemOrders_: BigInt!
outstandingRedeemOrdersCurrency_: BigInt!

fulfilledInvestOrders_: BigInt!
fulfilledRedeemOrders_: BigInt!
fulfilledRedeemOrdersCurrency_: BigInt!

yield30DaysAnnualized: BigInt
yield90DaysAnnualized: BigInt
Expand Down Expand Up @@ -171,8 +176,10 @@ type EpochState @entity {

outstandingInvestOrders: BigInt!
outstandingRedeemOrders: BigInt!
outstandingRedeemOrdersCurrency: BigInt!
fulfilledInvestOrders: BigInt
fulfilledRedeemOrders: BigInt
fulfilledRedeemOrdersCurrency: BigInt

investFulfillment: BigInt
redeemFulfillment: BigInt
Expand Down Expand Up @@ -244,20 +251,31 @@ enum LoanStatus {
CLOSED
}

#UNUSED
type Loan @entity {
id: ID!
enum LoanType {
BulletLoan
CreditLine
CreditLineWithMaturity
}

type Loan @entity {
id: ID! # poolId - loanId
createdAt: Date!

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

type: LoanType
spec: String

interestRatePerSec: BigInt
outstandingDebt: BigInt

totalBorrowed: BigInt
totalRepaid: BigInt

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

pool: Pool!
Expand All @@ -271,13 +289,16 @@ enum BorrowerTransactionType {
CLOSED
}

#UNUSED
type BorrowerTransaction @entity {
id: ID!
id: ID! # extrinsic hash - epoch number - transaction type
timestamp: Date!
pool: Pool! @index
hash: String!

account: Account!
epochNumber: Int!
loan: Loan!
accountId: String!
epochNumber: Int! @index
epoch: Epoch!
loan: Loan! @index
type: BorrowerTransactionType!

# only applies to BORROWED and REPAID transactions
Expand Down
18 changes: 15 additions & 3 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 All @@ -134,7 +146,7 @@ export type TokensTransferEvent = ITuple<[TokensCurrencyId, AccountId32, Account

export type ExtendedRpc = typeof api.rpc & {
pools: {
trancheTokenPrice: PromiseRpcResult<AugmentedRpc<(poolId: u64, trancheId: U8aFixed) => Observable<u128>>>
trancheTokenPrice: PromiseRpcResult<AugmentedRpc<(poolId: u64, trancheId: number[]) => Observable<u128>>>
trancheTokenPrices: PromiseRpcResult<AugmentedRpc<(poolId: u64) => Observable<Vec<u128>>>>
}
}
137 changes: 129 additions & 8 deletions src/mappings/handlers/loansHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,134 @@
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'
import { BorrowerTransactionService } from '../services/borrowerTransactionService'

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()}`)
const poolService = await PoolService.getById(poolId.toString())
await poolService.increaseTotalBorrowings(amount.toBigInt())
await poolService.save()
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 pool = await PoolService.getById(poolId.toString())

const loan = await LoanService.init(poolId.toString(), loanId.toString(), event.block.timestamp)
await loan.save()

const bt = await BorrowerTransactionService.created({
poolId: poolId.toString(),
loanId: loanId.toString(),
address: event.extrinsic.extrinsic.signer.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
})
await bt.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()}`)
const pool = await PoolService.getById(poolId.toString())

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

const bt = await BorrowerTransactionService.borrowed({
poolId: poolId.toString(),
loanId: loanId.toString(),
address: event.extrinsic.extrinsic.signer.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
amount: amount.toBigInt(),
})
await bt.save()

// Update poolState info
await pool.increaseTotalBorrowings(amount.toBigInt())
await pool.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 pool = await PoolService.getById(poolId.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()

const bt = await BorrowerTransactionService.priced({
poolId: poolId.toString(),
loanId: loanId.toString(),
address: event.extrinsic.extrinsic.signer.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
})
await bt.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 pool = await PoolService.getById(poolId.toString())

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

const bt = await BorrowerTransactionService.repaid({
poolId: poolId.toString(),
loanId: loanId.toString(),
address: event.extrinsic.extrinsic.signer.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
amount: amount.toBigInt(),
})
await bt.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 pool = await PoolService.getById(poolId.toString())

const loan = await LoanService.getById(poolId.toString(), loanId.toString())
await loan.close()
await loan.save()

const bt = await BorrowerTransactionService.repaid({
poolId: poolId.toString(),
loanId: loanId.toString(),
address: event.extrinsic.extrinsic.signer.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
})
await bt.save()
}
38 changes: 20 additions & 18 deletions src/mappings/handlers/ormlTokensHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { SubstrateEvent } from '@subql/types'
import { errorHandler } from '../../helpers/errorHandler'
import { TokensTransferEvent } from '../../helpers/types'
import { CurrencyService } from '../services/currencyService'
import { InvestorTransactionService } from '../services/investorTransactionService'
import { PoolService } from '../services/poolService'
import { TrancheService } from '../services/trancheService'

export const handleTokenTransfer = errorHandler(_handleTokenTransfer)
async function _handleTokenTransfer(event: SubstrateEvent<TokensTransferEvent>): Promise<void> {
Expand All @@ -25,30 +27,30 @@ async function _handleTokenTransfer(event: SubstrateEvent<TokensTransferEvent>):

// Get corresponding pool
const pool = await PoolService.getById(poolId.toString())
const tranche = await TrancheService.getById(poolId.toString(), trancheId.toHex())

// Update tranche price
await tranche.updatePriceFromRpc()
await tranche.save()

const orderData = {
poolId: poolId.toString(),
trancheId: trancheId.toString(),
epochNumber: pool.pool.currentEpoch,
hash: event.extrinsic.extrinsic.hash.toString(),
timestamp: event.block.timestamp,
digits: (await CurrencyService.getById(pool.pool.currencyId)).currency.decimals,
price: tranche.trancheState.price,
amount: amount.toBigInt(),
}

// CREATE 2 TRANSFERS FOR FROM AND TO ADDRESS
// with from create TRANSFER_OUT
const txOut = InvestorTransactionService.transferOut(
poolId.toString(),
trancheId.toString(),
pool.pool.currentEpoch,
from.toString(),
event.extrinsic.extrinsic.hash.toString(),
amount.toBigInt(),
event.block.timestamp
)
const txOut = InvestorTransactionService.transferOut({ ...orderData, address: from.toString() })
await txOut.save()

// with to create TRANSFER_IN
const txIn = InvestorTransactionService.transferIn(
poolId.toString(),
trancheId.toString(),
pool.pool.currentEpoch,
to.toString(),
event.extrinsic.extrinsic.hash.toString(),
amount.toBigInt(),
event.block.timestamp
)
const txIn = InvestorTransactionService.transferIn({ ...orderData, address: to.toString() })
await txIn.save()
}
}
Loading

0 comments on commit 0bf23cd

Please sign in to comment.