From 268d1c9efb654ba1cfb0b98ce69fd38e8b90a4cd Mon Sep 17 00:00:00 2001 From: Filippo Date: Wed, 13 Nov 2024 12:23:28 +0100 Subject: [PATCH 1/3] 256 refactor to enable strict mode (#266) * chore: enable strict mode * fix: matching ipfs metadata string * fix: improve handling of undefined get services * fix: enhanced in memory snapshotting * fix: currency initialization * chore: upgrade indexer version * fix: exceptions handling blockHandlers * fix: snapshotters tests --- src/helpers/stateSnapshot.test.ts | 54 +++------- src/helpers/stateSnapshot.ts | 96 +----------------- src/mappings/handlers/blockHandlers.ts | 132 +++++++++++++++---------- src/mappings/handlers/ethHandlers.ts | 4 +- 4 files changed, 102 insertions(+), 184 deletions(-) diff --git a/src/helpers/stateSnapshot.test.ts b/src/helpers/stateSnapshot.test.ts index 6fe0631..40c34d7 100644 --- a/src/helpers/stateSnapshot.test.ts +++ b/src/helpers/stateSnapshot.test.ts @@ -1,7 +1,7 @@ import { SubstrateBlock } from '@subql/types' import { PoolService } from '../mappings/services/poolService' -import { substrateStateSnapshotter } from './stateSnapshot' -import { Pool, PoolSnapshot } from '../types' +import { BlockInfo, statesSnapshotter } from './stateSnapshot' +import { PoolSnapshot } from '../types' import { getPeriodStart } from './timekeeperService' // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -25,40 +25,24 @@ describe('Given a populated pool,', () => { set.mockReset() getByFields.mockReset() getByFields.mockReturnValue([pool]) - await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block) - expect(store.getByFields).toHaveBeenCalledWith('Pool', [['blockchainId', '=', '0']], expect.anything()) + const blockInfo: BlockInfo = { timestamp, number: blockNumber } + await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo) + expect(store.set).toHaveBeenCalledTimes(2) expect(store.set).toHaveBeenNthCalledWith(1, 'Pool', poolId, expect.anything()) - expect(store.set).toHaveBeenNthCalledWith(2, 'PoolSnapshot', `${poolId}-11246`, expect.anything()) + expect(store.set).toHaveBeenNthCalledWith(2, 'PoolSnapshot', `${poolId}-${blockNumber}`, expect.anything()) }) test('when a snapshot is taken, then the timestamp and blocknumber are added', async () => { set.mockReset() getByFields.mockReset() getByFields.mockReturnValue([pool]) - await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block) + const blockInfo: BlockInfo = { timestamp, number: blockNumber } + await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo) expect(store.set).toHaveBeenNthCalledWith( 2, 'PoolSnapshot', - `${poolId}-11246`, - expect.objectContaining({ timestamp: block.timestamp, blockNumber: 11246 }) - ) - }) - - test('when filters are specified, then the correct values are fetched', async () => { - set.mockReset() - getByFields.mockReset() - getByFields.mockReturnValue([pool]) - await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block, [ - ['isActive', '=', true], - ]) - expect(store.getByFields).toHaveBeenNthCalledWith( - 1, - 'Pool', - [ - ['blockchainId', '=', '0'], - ['isActive', '=', true], - ], - expect.anything() + `${poolId}-${blockNumber}`, + expect.objectContaining({ timestamp: block.timestamp, blockNumber: blockNumber }) ) }) @@ -66,19 +50,12 @@ describe('Given a populated pool,', () => { set.mockReset() getByFields.mockReset() getByFields.mockReturnValue([pool]) - await substrateStateSnapshotter( - 'periodId', - periodId, - Pool, - PoolSnapshot, - block, - [['type', '=', 'ALL']], - 'poolId' - ) + const blockInfo: BlockInfo = { timestamp, number: blockNumber } + await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo, 'poolId') expect(store.set).toHaveBeenNthCalledWith( 2, 'PoolSnapshot', - `${poolId}-11246`, + `${poolId}-${blockNumber}`, expect.objectContaining({ poolId: poolId }) ) }) @@ -101,13 +78,14 @@ describe('Given a pool with non zero accumulators, ', () => { Object.assign(pool, accumulatedProps) - await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block) + const blockInfo: BlockInfo = { timestamp, number: 11246 } + await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo) expect(store.set).toHaveBeenNthCalledWith(1, 'Pool', poolId, expect.objectContaining(zeroedProps)) expect(store.set).toHaveBeenNthCalledWith( 2, 'PoolSnapshot', - `${poolId}-11246`, + `${poolId}-${blockNumber}`, expect.objectContaining(zeroedProps) ) }) diff --git a/src/helpers/stateSnapshot.ts b/src/helpers/stateSnapshot.ts index 9c41e7e..4ac095e 100644 --- a/src/helpers/stateSnapshot.ts +++ b/src/helpers/stateSnapshot.ts @@ -1,7 +1,4 @@ -import { paginatedGetter } from './paginatedGetter' import type { Entity, FieldsExpression, FunctionPropertyNames, GetOptions } from '@subql/types-core' -import { EthereumBlock } from '@subql/types-ethereum' -import { SubstrateBlock } from '@subql/types' /** * Creates a snapshot of a generic stateModel to a snapshotModel. * A snapshotModel has the same fields as the originating stateModel, however a timestamp and a blockNumber are added. @@ -16,72 +13,6 @@ import { SubstrateBlock } from '@subql/types' * @param resetPeriodStates - (optional) reset properties ending in ByPeriod to 0 after snapshot (defaults to true). * @returns A promise resolving when all state manipulations in the DB is completed */ -async function stateSnapshotter>( - relationshipField: StringForeignKeys, - relationshipId: string, - stateModel: EntityClass, - snapshotModel: EntityClass, - block: { number: number; timestamp: Date }, - filters?: FieldsExpression>[], - fkReferenceField?: StringForeignKeys, - resetPeriodStates = true, - blockchainId = '0' -): Promise { - const entitySaves: Promise[] = [] - logger.info(`Performing snapshots of ${stateModel.prototype._name} for blockchainId ${blockchainId}`) - const filter = [['blockchainId', '=', blockchainId]] as FieldsExpression>[] - if (filters) filter.push(...filters) - const stateEntities = await paginatedGetter(stateModel, filter) - if (stateEntities.length === 0) logger.info(`No ${stateModel.prototype._name} to snapshot!`) - for (const stateEntity of stateEntities) { - const blockNumber = block.number - const snapshot: SnapshottedEntity = { - ...stateEntity, - id: `${stateEntity.id}-${blockNumber}`, - timestamp: block.timestamp, - blockNumber: blockNumber, - [relationshipField]: relationshipId, - } - logger.info(`Snapshotting ${stateModel.prototype._name}: ${stateEntity.id}`) - const snapshotEntity = snapshotModel.create(snapshot as U) - if (fkReferenceField) snapshotEntity[fkReferenceField] = stateEntity.id as U[StringForeignKeys] - const propNames = Object.getOwnPropertyNames(stateEntity) - const propNamesToReset = propNames.filter((propName) => propName.endsWith('ByPeriod')) as ResettableKeys[] - if (resetPeriodStates) { - for (const propName of propNamesToReset) { - logger.debug(`resetting ${stateEntity._name?.toLowerCase()}.${propName} to 0`) - stateEntity[propName] = BigInt(0) as T[ResettableKeys] - } - entitySaves.push(stateEntity.save()) - } - entitySaves.push(snapshotEntity.save()) - } - return Promise.all(entitySaves) -} -export function evmStateSnapshotter>( - relationshipField: StringForeignKeys, - relationshipId: string, - stateModel: EntityClass, - snapshotModel: EntityClass, - block: EthereumBlock, - filters?: FieldsExpression>[], - fkReferenceName?: StringForeignKeys, - resetPeriodStates = true -): Promise { - const formattedBlock = { number: block.number, timestamp: new Date(Number(block.timestamp) * 1000) } - return stateSnapshotter( - relationshipField, - relationshipId, - stateModel, - snapshotModel, - formattedBlock, - filters, - fkReferenceName, - resetPeriodStates, - '1' - ) -} - export async function statesSnapshotter>( relationshipField: StringForeignKeys, relationshipId: string, @@ -116,35 +47,12 @@ export async function statesSnapshotter>( - relationshipField: StringForeignKeys, - relationshipId: string, - stateModel: EntityClass, - snapshotModel: EntityClass, - block: SubstrateBlock, - filters?: FieldsExpression>[], - fkReferenceName?: StringForeignKeys, - resetPeriodStates = true -): Promise { - if (!block.timestamp) throw new Error('Missing block timestamp') - const formattedBlock = { number: block.block.header.number.toNumber(), timestamp: block.timestamp } - return stateSnapshotter( - relationshipField, - relationshipId, - stateModel, - snapshotModel, - formattedBlock, - filters, - fkReferenceName, - resetPeriodStates, - '0' - ) -} - type ResettableKeyFormat = `${string}ByPeriod` type ForeignKeyFormat = `${string}Id` type ResettableKeys = { [K in keyof T]: K extends ResettableKeyFormat ? K : never }[keyof T] diff --git a/src/mappings/handlers/blockHandlers.ts b/src/mappings/handlers/blockHandlers.ts index db9eecd..0b05f2f 100644 --- a/src/mappings/handlers/blockHandlers.ts +++ b/src/mappings/handlers/blockHandlers.ts @@ -52,9 +52,18 @@ async function _handleBlock(block: SubstrateBlock): Promise { const pools = await PoolService.getCfgActivePools() for (const pool of pools) { logger.info(` ## Updating pool ${pool.id} states...`) - if (!pool.currentEpoch) throw new Error('Pool currentEpoch not set') + + if (!pool.currentEpoch) { + logger.error(`Pool currentEpoch not set for ${pool.id}, skipping...`) + continue + } + const currentEpoch = await EpochService.getById(pool.id, pool.currentEpoch) - if (!currentEpoch) throw new Error(`Current epoch ${pool.currentEpoch} for pool ${pool.id} not found`) + if (!currentEpoch) { + logger.error(`Current epoch ${pool.currentEpoch} for pool ${pool.id} not found, skipping pool`) + continue + } + await pool.updateState() await pool.resetDebtOverdue() @@ -63,8 +72,14 @@ async function _handleBlock(block: SubstrateBlock): Promise { const trancheData = await pool.getTranches() const trancheTokenPrices = await pool.getTrancheTokenPrices() for (const tranche of tranches) { - if (typeof tranche.index !== 'number') throw new Error('Tranche index not set') - if (!trancheTokenPrices) break + if (typeof tranche.index !== 'number') { + logger.error('Tranche index not set, skipping tranche') + continue + } + if (!trancheTokenPrices) { + logger.error('trancheTokenPrices not available, skipping tranche updates') + break + } await tranche.updatePrice(trancheTokenPrices[tranche.index].toBigInt(), block.block.header.number.toNumber()) await tranche.updateSupply() await tranche.updateDebt(trancheData[tranche.trancheId].debt) @@ -84,7 +99,12 @@ async function _handleBlock(block: SubstrateBlock): Promise { limit: 100, })) as TrancheBalanceService[] for (const trancheBalance of trancheBalances) { - if (!tranche.tokenPrice) throw new Error('Tranche token price not set') + if (typeof tranche.tokenPrice !== 'bigint') { + console.warn( + `tokenPrice not set, unable to update unrealizedProfit for trancheBalance ${trancheBalance.id}` + ) + continue + } const unrealizedProfit = await InvestorPositionService.computeUnrealizedProfitAtPrice( trancheBalance.accountId, tranche.id, @@ -95,50 +115,61 @@ async function _handleBlock(block: SubstrateBlock): Promise { } } // Asset operations - const activeLoanData = await pool.getPortfolio() + const activeAssetData = await pool.getPortfolio() pool.resetOffchainCashValue() pool.resetUnrealizedProfit() - for (const loanId in activeLoanData) { - const asset = await AssetService.getById(pool.id, loanId) + for (const assetId in activeAssetData) { + const asset = await AssetService.getById(pool.id, assetId) if (!asset) continue - if (!asset.currentPrice) throw new Error('Asset current price not set') - if (!asset.notional) throw new Error('Asset notional not set') await asset.loadSnapshot(lastPeriodStart) - await asset.updateActiveAssetData(activeLoanData[loanId]) - await asset.updateUnrealizedProfit( - await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.currentPrice), - await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.notional) - ) + await asset.updateActiveAssetData(activeAssetData[assetId]) + if (asset.notional && asset.currentPrice) { + await asset.updateUnrealizedProfit( + await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.currentPrice), + await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.notional) + ) + } else { + console.warn(`Missing current price or notional, unable to update unrealized profit for asset ${assetId}`) + } await asset.save() assetsToSnapshot.push(asset) - if (typeof asset.interestAccruedByPeriod !== 'bigint') - throw new Error('Asset interest accrued by period not set') - await pool.increaseInterestAccrued(asset.interestAccruedByPeriod) - + if (typeof asset.interestAccruedByPeriod === 'bigint') { + await pool.increaseInterestAccrued(asset.interestAccruedByPeriod) + } else { + logger.warn(`interestAccruedByPeriod not set, unable to compute accrued interest for asset ${assetId}`) + } if (asset.isNonCash()) { - if (typeof asset.unrealizedProfitAtMarketPrice !== 'bigint') - throw new Error('Asset unrealized profit at market price not set') - if (typeof asset.unrealizedProfitAtNotional !== 'bigint') - throw new Error('Asset unrealized profit at notional not set') - if (typeof asset.unrealizedProfitByPeriod !== 'bigint') - throw new Error('Asset unrealized profit by period not set') - pool.increaseUnrealizedProfit( - asset.unrealizedProfitAtMarketPrice, - asset.unrealizedProfitAtNotional, - asset.unrealizedProfitByPeriod - ) + if ( + typeof asset.unrealizedProfitAtMarketPrice === 'bigint' && + typeof asset.unrealizedProfitAtNotional === 'bigint' && + typeof asset.unrealizedProfitByPeriod === 'bigint' + ) { + pool.increaseUnrealizedProfit( + asset.unrealizedProfitAtMarketPrice, + asset.unrealizedProfitAtNotional, + asset.unrealizedProfitByPeriod + ) + } else { + logger.warn(`Missing unrealized profit figures, unable to increase unrealized profit for asset ${assetId}`) + } } if (asset.isBeyondMaturity(block.timestamp)) { - if (typeof asset.outstandingDebt !== 'bigint') throw new Error('Asset outstanding debt not set') - pool.increaseDebtOverdue(asset.outstandingDebt) + if (typeof asset.outstandingDebt === 'bigint') { + pool.increaseDebtOverdue(asset.outstandingDebt) + } else { + logger.warn(`Unable to increase debt overdue, missing outstandingDebt for ${assetId}`) + } } if (asset.isOffchainCash()) { - if (typeof asset.presentValue !== 'bigint') throw new Error('Asset present value not set') - pool.increaseOffchainCashValue(asset.presentValue) + if (typeof asset.presentValue === 'bigint') { + pool.increaseOffchainCashValue(asset.presentValue) + } else { + logger.warn(`Asset present value not set, unable to increase offchain cash value for ${assetId}`) + } } } - await pool.updateNumberOfActiveAssets(BigInt(Object.keys(activeLoanData).length)) + await pool.updateNumberOfActiveAssets(BigInt(Object.keys(activeAssetData).length)) // NAV update requires updated offchain cash value await pool.updateNAV() @@ -156,20 +187,21 @@ async function _handleBlock(block: SubstrateBlock): Promise { await poolFee.save() poolFeesToSnapshot.push(poolFee) - if (typeof poolFee.sumAccruedAmountByPeriod !== 'bigint') - throw new Error('Pool fee sum accrued amount by period not set') - await pool.increaseAccruedFees(poolFee.sumAccruedAmountByPeriod) - - const poolFeeTransaction = PoolFeeTransactionService.accrue({ - poolId: pool.id, - feeId, - blockNumber, - amount: poolFee.sumAccruedAmountByPeriod, - epochId: currentEpoch.id, - hash: block.hash.toHex(), - timestamp: block.timestamp, - }) - await poolFeeTransaction.save() + if (typeof poolFee.sumAccruedAmountByPeriod === 'bigint') { + await pool.increaseAccruedFees(poolFee.sumAccruedAmountByPeriod) + const poolFeeTransaction = PoolFeeTransactionService.accrue({ + poolId: pool.id, + feeId, + blockNumber, + amount: poolFee.sumAccruedAmountByPeriod, + epochId: currentEpoch.id, + hash: block.hash.toHex(), + timestamp: block.timestamp, + }) + await poolFeeTransaction.save() + } else { + logger.warn(`sumAccruedAmountByPeriod not set. unable to increase accrued fees for ${poolFee.id}`) + } } const sumPoolFeesPendingAmount = await PoolFeeService.computeSumPendingFees(pool.id) await pool.updateSumPoolFeesPendingAmount(sumPoolFeesPendingAmount) @@ -180,7 +212,7 @@ async function _handleBlock(block: SubstrateBlock): Promise { logger.info('## Performing snapshots...') const blockInfo = { number: block.block.header.number.toNumber(), timestamp: block.timestamp } - await statesSnapshotter('periodId', period.id, pools, PoolSnapshot, blockInfo, 'poolId') + await statesSnapshotter('periodId', period.id, poolsToSnapshot, PoolSnapshot, blockInfo, 'poolId') await statesSnapshotter('periodId', period.id, tranchesToSnapshot, TrancheSnapshot, blockInfo, 'trancheId') await statesSnapshotter('periodId', period.id, assetsToSnapshot, AssetSnapshot, blockInfo, 'assetId') await statesSnapshotter('periodId', period.id, poolFeesToSnapshot, PoolFeeSnapshot, blockInfo, 'poolFeeId') diff --git a/src/mappings/handlers/ethHandlers.ts b/src/mappings/handlers/ethHandlers.ts index a67629a..c6b6de9 100644 --- a/src/mappings/handlers/ethHandlers.ts +++ b/src/mappings/handlers/ethHandlers.ts @@ -46,7 +46,7 @@ async function _handleEthBlock(block: EthereumBlock): Promise { PoolService['id'], { pool: PoolService - tinlakePool: typeof tinlakePools[0] + tinlakePool: (typeof tinlakePools)[0] latestNavFeed?: ContractArray latestReserve?: ContractArray } @@ -152,7 +152,7 @@ async function _handleEthBlock(block: EthereumBlock): Promise { // Take snapshots const blockInfo: BlockInfo = { timestamp: date, number: block.number } - const poolsToSnapshot: PoolService[] = Object.values(processedPools).map(e => e.pool) + const poolsToSnapshot: PoolService[] = Object.values(processedPools).map((e) => e.pool) await statesSnapshotter('periodId', snapshotPeriod.id, poolsToSnapshot, PoolSnapshot, blockInfo, 'poolId') //Update tracking of period and continue From 99480daf1f33aaa57f04e95061dbf760bef21b9d Mon Sep 17 00:00:00 2001 From: Filippo Date: Wed, 13 Nov 2024 22:55:44 +0100 Subject: [PATCH 2/3] fix: missingId error (#267) --- src/helpers/stateSnapshot.ts | 12 +- src/mappings/handlers/blockHandlers.ts | 359 +++++++++--------- src/mappings/handlers/ethHandlers.ts | 4 +- src/mappings/handlers/poolFeesHandlers.ts | 2 +- src/mappings/services/accountService.ts | 4 +- src/mappings/services/assetService.ts | 4 +- src/mappings/services/currencyService.ts | 4 +- src/mappings/services/epochService.ts | 2 +- .../services/investorTransactionService.ts | 4 +- src/mappings/services/poolFeeService.ts | 11 +- src/mappings/services/poolService.test.ts | 2 +- src/mappings/services/poolService.ts | 13 +- .../services/trancheBalanceService.ts | 4 +- src/mappings/services/trancheService.ts | 4 +- 14 files changed, 220 insertions(+), 209 deletions(-) diff --git a/src/helpers/stateSnapshot.ts b/src/helpers/stateSnapshot.ts index 4ac095e..e635015 100644 --- a/src/helpers/stateSnapshot.ts +++ b/src/helpers/stateSnapshot.ts @@ -47,8 +47,6 @@ export async function statesSnapshotter (typeof value === 'bigint' ? value.toString() : value) // return everything else unchanged + ) + ) +} diff --git a/src/mappings/handlers/blockHandlers.ts b/src/mappings/handlers/blockHandlers.ts index 0b05f2f..9c30518 100644 --- a/src/mappings/handlers/blockHandlers.ts +++ b/src/mappings/handlers/blockHandlers.ts @@ -14,6 +14,8 @@ import { EpochService } from '../services/epochService' import { SnapshotPeriodService } from '../services/snapshotPeriodService' import { TrancheBalanceService } from '../services/trancheBalanceService' import { InvestorPositionService } from '../services/investorPositionService' +import { CurrencyService } from '../services/currencyService' +import { BlockchainService } from '../services/blockchainService' const timekeeper = TimekeeperService.init() @@ -25,200 +27,203 @@ async function _handleBlock(block: SubstrateBlock): Promise { const blockNumber = block.block.header.number.toNumber() const newPeriod = (await timekeeper).processBlock(block.timestamp) - if (newPeriod) { - const specVersion = api.runtimeVersion.specVersion.toNumber() - logger.info( - `# It's a new period on block ${blockNumber}: ${block.timestamp.toISOString()} (specVersion: ${specVersion})` - ) - - const period = SnapshotPeriodService.init(blockPeriodStart) - await period.save() - - const lastPeriodStart = new Date(period.start.valueOf() - SNAPSHOT_INTERVAL_SECONDS * 1000) - const daysAgo7 = new Date(period.start.valueOf() - 7 * 24 * 3600 * 1000) - const daysAgo30 = new Date(period.start.valueOf() - 30 * 24 * 3600 * 1000) - const daysAgo90 = new Date(period.start.valueOf() - 90 * 24 * 3600 * 1000) - const beginningOfMonth = new Date(period.year, period.month, 1) - const quarter = Math.floor(period.month / 3) - const beginningOfQuarter = new Date(period.year, quarter * 3, 1) - const beginningOfYear = new Date(period.year, 0, 1) - - const poolsToSnapshot: PoolService[] = [] - const tranchesToSnapshot: TrancheService[] = [] - const assetsToSnapshot: AssetService[] = [] - const poolFeesToSnapshot: PoolFeeService[] = [] - - // Update Pool States - const pools = await PoolService.getCfgActivePools() - for (const pool of pools) { - logger.info(` ## Updating pool ${pool.id} states...`) - - if (!pool.currentEpoch) { - logger.error(`Pool currentEpoch not set for ${pool.id}, skipping...`) - continue - } + if (!newPeriod) return + + const specVersion = api.runtimeVersion.specVersion.toNumber() + logger.info( + `# It's a new period on block ${blockNumber}: ${block.timestamp.toISOString()} (specVersion: ${specVersion})` + ) + const _blockchain = BlockchainService.getOrInit(chainId) + const period = SnapshotPeriodService.init(blockPeriodStart) + await period.save() + + const lastPeriodStart = new Date(period.start.valueOf() - SNAPSHOT_INTERVAL_SECONDS * 1000) + const daysAgo7 = new Date(period.start.valueOf() - 7 * 24 * 3600 * 1000) + const daysAgo30 = new Date(period.start.valueOf() - 30 * 24 * 3600 * 1000) + const daysAgo90 = new Date(period.start.valueOf() - 90 * 24 * 3600 * 1000) + const beginningOfMonth = new Date(period.year, period.month, 1) + const quarter = Math.floor(period.month / 3) + const beginningOfQuarter = new Date(period.year, quarter * 3, 1) + const beginningOfYear = new Date(period.year, 0, 1) + + const poolsToSnapshot: PoolService[] = [] + const tranchesToSnapshot: TrancheService[] = [] + const assetsToSnapshot: AssetService[] = [] + const poolFeesToSnapshot: PoolFeeService[] = [] + + // Update Pool States + const pools = await PoolService.getCfgActivePools() + for (const pool of pools) { + const currency = (await CurrencyService.getByPoolId(pool.id, { limit: 1 })).pop() + logger.info(` ## Updating pool ${pool.id} states...`) + + if (!pool.currentEpoch) { + logger.error(`Pool currentEpoch not set for ${pool.id}, skipping...`) + continue + } - const currentEpoch = await EpochService.getById(pool.id, pool.currentEpoch) - if (!currentEpoch) { - logger.error(`Current epoch ${pool.currentEpoch} for pool ${pool.id} not found, skipping pool`) - continue - } + const currentEpoch = await EpochService.getById(pool.id, pool.currentEpoch) + if (!currentEpoch) { + logger.error(`Current epoch ${pool.currentEpoch} for pool ${pool.id} not found, skipping pool`) + continue + } - await pool.updateState() - await pool.resetDebtOverdue() + await pool.updateState() + await pool.resetDebtOverdue() - // Update tranche states - const tranches = await TrancheService.getActivesByPoolId(pool.id) - const trancheData = await pool.getTranches() - const trancheTokenPrices = await pool.getTrancheTokenPrices() - for (const tranche of tranches) { - if (typeof tranche.index !== 'number') { - logger.error('Tranche index not set, skipping tranche') + // Update tranche states + const tranches = await TrancheService.getActivesByPoolId(pool.id) + const trancheData = await pool.getTranches() + const trancheTokenPrices = await pool.getTrancheTokenPrices() + for (const tranche of tranches) { + if (typeof tranche.index !== 'number') { + logger.error('Tranche index not set, skipping tranche') + continue + } + if (!trancheTokenPrices) { + logger.error('trancheTokenPrices not available, skipping tranche updates') + break + } + await tranche.updatePrice(trancheTokenPrices[tranche.index].toBigInt(), block.block.header.number.toNumber()) + await tranche.updateSupply() + await tranche.updateDebt(trancheData[tranche.trancheId].debt) + await tranche.computeYield('yieldSinceLastPeriod', lastPeriodStart) + await tranche.computeYield('yieldSinceInception') + await tranche.computeYield('yieldYTD', beginningOfYear) + await tranche.computeYield('yieldQTD', beginningOfQuarter) + await tranche.computeYield('yieldMTD', beginningOfMonth) + await tranche.computeYieldAnnualized('yield7DaysAnnualized', period.start, daysAgo7) + await tranche.computeYieldAnnualized('yield30DaysAnnualized', period.start, daysAgo30) + await tranche.computeYieldAnnualized('yield90DaysAnnualized', period.start, daysAgo90) + await tranche.save() + tranchesToSnapshot.push(tranche) + + // Compute TrancheBalances Unrealized Profit + const trancheBalances = (await TrancheBalanceService.getByTrancheId(tranche.id, { + limit: 100, + })) as TrancheBalanceService[] + for (const trancheBalance of trancheBalances) { + if (typeof tranche.tokenPrice !== 'bigint') { + console.warn(`tokenPrice not set, unable to update unrealizedProfit for trancheBalance ${trancheBalance.id}`) continue } - if (!trancheTokenPrices) { - logger.error('trancheTokenPrices not available, skipping tranche updates') - break - } - await tranche.updatePrice(trancheTokenPrices[tranche.index].toBigInt(), block.block.header.number.toNumber()) - await tranche.updateSupply() - await tranche.updateDebt(trancheData[tranche.trancheId].debt) - await tranche.computeYield('yieldSinceLastPeriod', lastPeriodStart) - await tranche.computeYield('yieldSinceInception') - await tranche.computeYield('yieldYTD', beginningOfYear) - await tranche.computeYield('yieldQTD', beginningOfQuarter) - await tranche.computeYield('yieldMTD', beginningOfMonth) - await tranche.computeYieldAnnualized('yield7DaysAnnualized', period.start, daysAgo7) - await tranche.computeYieldAnnualized('yield30DaysAnnualized', period.start, daysAgo30) - await tranche.computeYieldAnnualized('yield90DaysAnnualized', period.start, daysAgo90) - await tranche.save() - tranchesToSnapshot.push(tranche) - - // Compute TrancheBalances Unrealized Profit - const trancheBalances = (await TrancheBalanceService.getByTrancheId(tranche.id, { - limit: 100, - })) as TrancheBalanceService[] - for (const trancheBalance of trancheBalances) { - if (typeof tranche.tokenPrice !== 'bigint') { - console.warn( - `tokenPrice not set, unable to update unrealizedProfit for trancheBalance ${trancheBalance.id}` - ) - continue - } - const unrealizedProfit = await InvestorPositionService.computeUnrealizedProfitAtPrice( - trancheBalance.accountId, - tranche.id, - tranche.tokenPrice - ) - await trancheBalance.updateUnrealizedProfit(unrealizedProfit) - await trancheBalance.save() - } + const unrealizedProfit = await InvestorPositionService.computeUnrealizedProfitAtPrice( + trancheBalance.accountId, + tranche.id, + tranche.tokenPrice + ) + await trancheBalance.updateUnrealizedProfit(unrealizedProfit) + await trancheBalance.save() + } + } + // Asset operations + const activeAssetData = await pool.getPortfolio() + pool.resetOffchainCashValue() + pool.resetUnrealizedProfit() + for (const assetId in activeAssetData) { + const asset = await AssetService.getById(pool.id, assetId) + if (!asset) continue + await asset.loadSnapshot(lastPeriodStart) + await asset.updateActiveAssetData(activeAssetData[assetId]) + if (asset.notional && asset.currentPrice) { + await asset.updateUnrealizedProfit( + await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.currentPrice), + await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.notional) + ) + } else { + console.warn(`Missing current price or notional, unable to update unrealized profit for asset ${assetId}`) + } + await asset.save() + assetsToSnapshot.push(asset) + + if (typeof asset.interestAccruedByPeriod === 'bigint') { + await pool.increaseInterestAccrued(asset.interestAccruedByPeriod) + } else { + logger.warn(`interestAccruedByPeriod not set, unable to compute accrued interest for asset ${assetId}`) } - // Asset operations - const activeAssetData = await pool.getPortfolio() - pool.resetOffchainCashValue() - pool.resetUnrealizedProfit() - for (const assetId in activeAssetData) { - const asset = await AssetService.getById(pool.id, assetId) - if (!asset) continue - await asset.loadSnapshot(lastPeriodStart) - await asset.updateActiveAssetData(activeAssetData[assetId]) - if (asset.notional && asset.currentPrice) { - await asset.updateUnrealizedProfit( - await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.currentPrice), - await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.notional) + if (asset.isNonCash()) { + if ( + typeof asset.unrealizedProfitAtMarketPrice === 'bigint' && + typeof asset.unrealizedProfitAtNotional === 'bigint' && + typeof asset.unrealizedProfitByPeriod === 'bigint' + ) { + pool.increaseUnrealizedProfit( + asset.unrealizedProfitAtMarketPrice, + asset.unrealizedProfitAtNotional, + asset.unrealizedProfitByPeriod ) } else { - console.warn(`Missing current price or notional, unable to update unrealized profit for asset ${assetId}`) + logger.warn(`Missing unrealized profit figures, unable to increase unrealized profit for asset ${assetId}`) } - await asset.save() - assetsToSnapshot.push(asset) - - if (typeof asset.interestAccruedByPeriod === 'bigint') { - await pool.increaseInterestAccrued(asset.interestAccruedByPeriod) + } + if (asset.isBeyondMaturity(block.timestamp)) { + if (typeof asset.outstandingDebt === 'bigint') { + pool.increaseDebtOverdue(asset.outstandingDebt) } else { - logger.warn(`interestAccruedByPeriod not set, unable to compute accrued interest for asset ${assetId}`) - } - if (asset.isNonCash()) { - if ( - typeof asset.unrealizedProfitAtMarketPrice === 'bigint' && - typeof asset.unrealizedProfitAtNotional === 'bigint' && - typeof asset.unrealizedProfitByPeriod === 'bigint' - ) { - pool.increaseUnrealizedProfit( - asset.unrealizedProfitAtMarketPrice, - asset.unrealizedProfitAtNotional, - asset.unrealizedProfitByPeriod - ) - } else { - logger.warn(`Missing unrealized profit figures, unable to increase unrealized profit for asset ${assetId}`) - } - } - if (asset.isBeyondMaturity(block.timestamp)) { - if (typeof asset.outstandingDebt === 'bigint') { - pool.increaseDebtOverdue(asset.outstandingDebt) - } else { - logger.warn(`Unable to increase debt overdue, missing outstandingDebt for ${assetId}`) - } - } - if (asset.isOffchainCash()) { - if (typeof asset.presentValue === 'bigint') { - pool.increaseOffchainCashValue(asset.presentValue) - } else { - logger.warn(`Asset present value not set, unable to increase offchain cash value for ${assetId}`) - } + logger.warn(`Unable to increase debt overdue, missing outstandingDebt for ${assetId}`) } } - await pool.updateNumberOfActiveAssets(BigInt(Object.keys(activeAssetData).length)) - - // NAV update requires updated offchain cash value - await pool.updateNAV() - - //PoolFees operations - const accruedFees = await pool.getAccruedFees() - for (const accruals of accruedFees) { - const [feeId, pending, disbursement] = accruals - const poolFee = await PoolFeeService.getById(pool.id, feeId) - if (!poolFee) { - logger.error(`Unable to retrieve PoolFee ${pool.id}-${feeId}, skipping accruals!`) - continue - } - await poolFee.updateAccruals(pending, disbursement) - await poolFee.save() - poolFeesToSnapshot.push(poolFee) - - if (typeof poolFee.sumAccruedAmountByPeriod === 'bigint') { - await pool.increaseAccruedFees(poolFee.sumAccruedAmountByPeriod) - const poolFeeTransaction = PoolFeeTransactionService.accrue({ - poolId: pool.id, - feeId, - blockNumber, - amount: poolFee.sumAccruedAmountByPeriod, - epochId: currentEpoch.id, - hash: block.hash.toHex(), - timestamp: block.timestamp, - }) - await poolFeeTransaction.save() + if (asset.isOffchainCash()) { + if (typeof asset.presentValue === 'bigint') { + pool.increaseOffchainCashValue(asset.presentValue) } else { - logger.warn(`sumAccruedAmountByPeriod not set. unable to increase accrued fees for ${poolFee.id}`) + logger.warn(`Asset present value not set, unable to increase offchain cash value for ${assetId}`) } } - const sumPoolFeesPendingAmount = await PoolFeeService.computeSumPendingFees(pool.id) - await pool.updateSumPoolFeesPendingAmount(sumPoolFeesPendingAmount) - await pool.save() - poolsToSnapshot.push(pool) - logger.info(`## Pool ${pool.id} states update completed!`) } + await pool.updateNumberOfActiveAssets(BigInt(Object.keys(activeAssetData).length)) - logger.info('## Performing snapshots...') - const blockInfo = { number: block.block.header.number.toNumber(), timestamp: block.timestamp } - await statesSnapshotter('periodId', period.id, poolsToSnapshot, PoolSnapshot, blockInfo, 'poolId') - await statesSnapshotter('periodId', period.id, tranchesToSnapshot, TrancheSnapshot, blockInfo, 'trancheId') - await statesSnapshotter('periodId', period.id, assetsToSnapshot, AssetSnapshot, blockInfo, 'assetId') - await statesSnapshotter('periodId', period.id, poolFeesToSnapshot, PoolFeeSnapshot, blockInfo, 'poolFeeId') - logger.info('## Snapshotting completed!') + // NAV update requires updated offchain cash value + if (currency) { + await pool.updateNAV(currency.decimals) + } else { + logger.warn(`Cannot update NAV for pool ${pool.id} as NO currency found / initialised`) + } - //Update tracking of period and continue - await (await timekeeper).update(period.start) + //PoolFees operations + const accruedFees = await pool.getAccruedFees() + for (const accruals of accruedFees) { + const [feeId, pending, disbursement] = accruals + const poolFee = await PoolFeeService.getById(pool.id, feeId) + if (!poolFee) { + logger.error(`Unable to retrieve PoolFee ${pool.id}-${feeId}, skipping accruals!`) + continue + } + await poolFee.updateAccruals(pending, disbursement) + await poolFee.save() + poolFeesToSnapshot.push(poolFee) + + if (typeof poolFee.sumAccruedAmountByPeriod === 'bigint') { + await pool.increaseAccruedFees(poolFee.sumAccruedAmountByPeriod) + const poolFeeTransaction = PoolFeeTransactionService.accrue({ + poolId: pool.id, + feeId, + blockNumber, + amount: poolFee.sumAccruedAmountByPeriod, + epochId: currentEpoch.id, + hash: block.hash.toHex(), + timestamp: block.timestamp, + }) + await poolFeeTransaction.save() + } else { + logger.warn(`sumAccruedAmountByPeriod not set. unable to increase accrued fees for ${poolFee.id}`) + } + } + const sumPoolFeesPendingAmount = await PoolFeeService.computeSumPendingFees(pool.id) + await pool.updateSumPoolFeesPendingAmount(sumPoolFeesPendingAmount) + await pool.save() + poolsToSnapshot.push(pool) + logger.info(`## Pool ${pool.id} states update completed!`) } + + logger.info('## Performing snapshots...') + const blockInfo = { number: block.block.header.number.toNumber(), timestamp: block.timestamp } + await statesSnapshotter('periodId', period.id, poolsToSnapshot, PoolSnapshot, blockInfo, 'poolId') + await statesSnapshotter('periodId', period.id, tranchesToSnapshot, TrancheSnapshot, blockInfo, 'trancheId') + await statesSnapshotter('periodId', period.id, assetsToSnapshot, AssetSnapshot, blockInfo, 'assetId') + await statesSnapshotter('periodId', period.id, poolFeesToSnapshot, PoolFeeSnapshot, blockInfo, 'poolFeeId') + logger.info('## Snapshotting completed!') + + //Update tracking of period and continue + await (await timekeeper).update(period.start) } diff --git a/src/mappings/handlers/ethHandlers.ts b/src/mappings/handlers/ethHandlers.ts index c6b6de9..f90faa3 100644 --- a/src/mappings/handlers/ethHandlers.ts +++ b/src/mappings/handlers/ethHandlers.ts @@ -119,7 +119,7 @@ async function _handleEthBlock(block: EthereumBlock): Promise { pool.id === ALT_1_POOL_ID && blockNumber > ALT_1_END_BLOCK ? BigInt(0) : (pool.portfolioValuation ?? BigInt(0)) + (pool.totalReserve ?? BigInt(0)) - await pool.updateNormalizedNAV() + await pool.updateNormalizedNAV(currency.decimals) logger.info(`Updating pool ${pool.id} with portfolioValuation: ${pool.portfolioValuation}`) } @@ -131,7 +131,7 @@ async function _handleEthBlock(block: EthereumBlock): Promise { : ReserveAbi__factory.createInterface().decodeFunctionResult('totalBalance', callResult.result)[0].toBigInt() pool.totalReserve = totalBalance pool.netAssetValue = (pool.portfolioValuation ?? BigInt(0)) + (pool.totalReserve ?? BigInt(0)) - await pool.updateNormalizedNAV() + await pool.updateNormalizedNAV(currency.decimals) logger.info(`Updating pool ${pool.id} with totalReserve: ${pool.totalReserve}`) } diff --git a/src/mappings/handlers/poolFeesHandlers.ts b/src/mappings/handlers/poolFeesHandlers.ts index 4aed302..ac5c38c 100644 --- a/src/mappings/handlers/poolFeesHandlers.ts +++ b/src/mappings/handlers/poolFeesHandlers.ts @@ -105,7 +105,7 @@ async function _handleFeeRemoved(event: SubstrateEvent): P } const poolFee = await PoolFeeService.delete(poolFeeData) - await poolFee.save() + if(poolFee) await poolFee.save() const poolFeeTransaction = PoolFeeTransactionService.delete(poolFeeData) await poolFeeTransaction.save() diff --git a/src/mappings/services/accountService.ts b/src/mappings/services/accountService.ts index abcd0e6..ef0da75 100644 --- a/src/mappings/services/accountService.ts +++ b/src/mappings/services/accountService.ts @@ -17,13 +17,13 @@ export class AccountService extends Account { } static async getOrInit(address: string, blockchainService = BlockchainService): Promise { - let account = (await this.get(address)) as AccountService | undefined + let account = (await this.get(address)) if (!account) { account = await this.init(address) await blockchainService.getOrInit(account.chainId) await account.save() } - return account + return account as AccountService } static evmToSubstrate(evmAddress: string, chainId: string) { diff --git a/src/mappings/services/assetService.ts b/src/mappings/services/assetService.ts index 8ee9910..43a7646 100644 --- a/src/mappings/services/assetService.ts +++ b/src/mappings/services/assetService.ts @@ -81,8 +81,8 @@ export class AssetService extends Asset { } static async getById(poolId: string, assetId: string) { - const asset = (await this.get(`${poolId}-${assetId}`)) as AssetService | undefined - return asset + const asset = (await this.get(`${poolId}-${assetId}`)) + return asset as AssetService } static async getByNftId(collectionId: string, itemId: string) { diff --git a/src/mappings/services/currencyService.ts b/src/mappings/services/currencyService.ts index eabca03..1d11b3d 100644 --- a/src/mappings/services/currencyService.ts +++ b/src/mappings/services/currencyService.ts @@ -17,7 +17,7 @@ export class CurrencyService extends Currency { static async getOrInit(chainId: string, currencyType: string, ...currencyValue: string[]) { const currencyId = currencyValue.length > 0 ? `${currencyType}-${currencyValue.join('-')}` : currencyType const id = `${chainId}-${currencyId}` - let currency = (await this.get(id)) as CurrencyService | undefined + let currency = (await this.get(id)) if (!currency) { const enumPayload = formatEnumPayload(currencyType, ...currencyValue) const assetMetadata = (await api.query.ormlAssetRegistry.metadata(enumPayload)) as Option @@ -33,7 +33,7 @@ export class CurrencyService extends Currency { static async getOrInitEvm(chainId: string, currencyType: string, symbol?: string, name?: string) { const currencyId = currencyType const id = `${chainId}-${currencyId}` - let currency: CurrencyService = (await this.get(id)) as CurrencyService + let currency = (await this.get(id)) if (!currency) { currency = this.init(chainId, currencyId, WAD_DIGITS, symbol, name) await currency.save() diff --git a/src/mappings/services/epochService.ts b/src/mappings/services/epochService.ts index ae3edd8..b3d6f20 100644 --- a/src/mappings/services/epochService.ts +++ b/src/mappings/services/epochService.ts @@ -43,7 +43,7 @@ export class EpochService extends Epoch { } static async getById(poolId: string, epochNr: number) { - const epoch = (await this.get(`${poolId}-${epochNr.toString()}`)) as EpochService + const epoch = (await this.get(`${poolId}-${epochNr.toString()}`)) as EpochService | undefined if (!epoch) return undefined const epochStates = await EpochState.getByEpochId(`${poolId}-${epochNr.toString(10)}`, { limit: 100 }) epoch.states = epochStates diff --git a/src/mappings/services/investorTransactionService.ts b/src/mappings/services/investorTransactionService.ts index 116b02c..3766a5a 100644 --- a/src/mappings/services/investorTransactionService.ts +++ b/src/mappings/services/investorTransactionService.ts @@ -162,8 +162,8 @@ export class InvestorTransactionService extends InvestorTransaction { } static async getById(hash: string) { - const tx = (await this.get(hash)) as InvestorTransactionService | undefined - return tx + const tx = await this.get(hash) + return tx as InvestorTransactionService | undefined } static computeTokenAmount(data: InvestorTransactionData) { diff --git a/src/mappings/services/poolFeeService.ts b/src/mappings/services/poolFeeService.ts index 0fbfe8f..53e4a4c 100644 --- a/src/mappings/services/poolFeeService.ts +++ b/src/mappings/services/poolFeeService.ts @@ -40,13 +40,13 @@ export class PoolFeeService extends PoolFee { blockchain = '0' ) { const { poolId, feeId } = data - let poolFee = (await this.get(`${poolId}-${feeId}`)) as PoolFeeService | undefined + let poolFee = await this.get(`${poolId}-${feeId}`) if (!poolFee) { poolFee = this.init(data, type, status, blockchain) } else { poolFee.status = PoolFeeStatus[status] } - return poolFee + return poolFee as PoolFeeService } static getById(poolId: string, feeId: string) { @@ -70,9 +70,12 @@ export class PoolFeeService extends PoolFee { logger.info(`Removing PoolFee ${data.feeId}`) const { poolId, feeId } = data const poolFee = await this.get(`${poolId}-${feeId}`) - if (!poolFee) throw new Error('Unable to remove PoolFee. PoolFee does not exist.') + if (!poolFee) { + logger.error('Unable to remove PoolFee. PoolFee does not exist.') + return + } poolFee.isActive = false - return poolFee + return poolFee as PoolFeeService } public charge(data: Omit & Required>) { diff --git a/src/mappings/services/poolService.test.ts b/src/mappings/services/poolService.test.ts index a9d7b5b..d81b468 100644 --- a/src/mappings/services/poolService.test.ts +++ b/src/mappings/services/poolService.test.ts @@ -86,7 +86,7 @@ describe('Given a new pool, when initialised', () => { describe('Given an existing pool,', () => { test.skip('when the nav is updated, then the value is fetched and set correctly', async () => { - await pool.updateNAV() + await pool.updateNAV(14) expect(api.query.loans.portfolioValuation).toHaveBeenCalled() expect(pool.portfolioValuation).toBe(BigInt(100000000000000)) }) diff --git a/src/mappings/services/poolService.ts b/src/mappings/services/poolService.ts index 0c6128d..14d3202 100644 --- a/src/mappings/services/poolService.ts +++ b/src/mappings/services/poolService.ts @@ -13,7 +13,6 @@ import { Pool } from '../../types' import { cid, readIpfs } from '../../helpers/ipfsFetch' import { EpochService } from './epochService' import { WAD_DIGITS } from '../../config' -import { CurrencyService } from './currencyService' import { assertPropInitialized } from '../../helpers/validation' export class PoolService extends Pool { @@ -214,7 +213,7 @@ export class PoolService extends Pool { return this } - public async updateNAV() { + public async updateNAV(poolCurrencyDecimals: number) { const specVersion = api.runtimeVersion.specVersion.toNumber() const specName = api.runtimeVersion.specName.toString() switch (specName) { @@ -225,7 +224,7 @@ export class PoolService extends Pool { await (specVersion < 1025 ? this.updateNAVQuery() : this.updateNAVCall()) break } - await this.updateNormalizedNAV() + await this.updateNormalizedNAV(poolCurrencyDecimals) return this } @@ -275,13 +274,9 @@ export class PoolService extends Pool { return this } - public async updateNormalizedNAV() { - assertPropInitialized(this, 'currencyId', 'string') + public async updateNormalizedNAV(poolCurrencyDecimals: number) { assertPropInitialized(this, 'netAssetValue', 'bigint') - - const currency = await CurrencyService.get(this.currencyId!) - if (!currency) throw new Error(`No currency with Id ${this.currencyId} found!`) - const digitsMismatch = WAD_DIGITS - currency.decimals + const digitsMismatch = WAD_DIGITS - poolCurrencyDecimals if (digitsMismatch === 0) { this.normalizedNAV = this.netAssetValue return this diff --git a/src/mappings/services/trancheBalanceService.ts b/src/mappings/services/trancheBalanceService.ts index 1056a6d..9cc45fb 100644 --- a/src/mappings/services/trancheBalanceService.ts +++ b/src/mappings/services/trancheBalanceService.ts @@ -26,11 +26,11 @@ export class TrancheBalanceService extends TrancheBalance { static getOrInit = async (address: string, poolId: string, trancheId: string) => { let trancheBalance = await this.getById(address, poolId, trancheId) - if (trancheBalance === undefined) { + if (!trancheBalance) { trancheBalance = this.init(address, poolId, trancheId) await trancheBalance.save() } - return trancheBalance + return trancheBalance as TrancheBalanceService } public investOrder(currencyAmount: bigint) { diff --git a/src/mappings/services/trancheService.ts b/src/mappings/services/trancheService.ts index 8ae371e..7a79d47 100644 --- a/src/mappings/services/trancheService.ts +++ b/src/mappings/services/trancheService.ts @@ -57,8 +57,8 @@ export class TrancheService extends Tranche { } static async getById(poolId: string, trancheId: string) { - const tranche = (await this.get(`${poolId}-${trancheId}`)) as TrancheService | undefined - return tranche + const tranche = (await this.get(`${poolId}-${trancheId}`)) + return tranche as TrancheService | undefined } static async getByPoolId(poolId: string): Promise { From e9faa04c95fd16aabca0959d74f44318cd17646d Mon Sep 17 00:00:00 2001 From: Jeroen <1748621+hieronx@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:24:00 +0100 Subject: [PATCH 3/3] feat: add initialised at timestamp to tranche balances (#264) * feat: add initialised at timestamp to tranche balances * chore: cosmetics * fix: timestamps init --------- Co-authored-by: Filippo Fontana --- schema.graphql | 2 ++ src/mappings/handlers/evmHandlers.ts | 10 ++++--- src/mappings/handlers/investmentsHandlers.ts | 28 ++++++++++++++++--- src/mappings/handlers/poolsHandlers.ts | 3 +- .../services/trancheBalanceService.ts | 7 +++-- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/schema.graphql b/schema.graphql index c66b140..cf8a365 100644 --- a/schema.graphql +++ b/schema.graphql @@ -410,6 +410,8 @@ type TrancheBalance @entity { pool: Pool! @index tranche: Tranche! @index + initialisedAt: Date! + pendingInvestCurrency: BigInt! claimableTrancheTokens: BigInt! diff --git a/src/mappings/handlers/evmHandlers.ts b/src/mappings/handlers/evmHandlers.ts index 9a7bf1a..5c40c3e 100644 --- a/src/mappings/handlers/evmHandlers.ts +++ b/src/mappings/handlers/evmHandlers.ts @@ -9,13 +9,10 @@ import { InvestorTransactionData, InvestorTransactionService } from '../services import { CurrencyService } from '../services/currencyService' import { BlockchainService } from '../services/blockchainService' import { CurrencyBalanceService } from '../services/currencyBalanceService' -import type { Provider } from '@ethersproject/providers' import { TrancheBalanceService } from '../services/trancheBalanceService' import { escrows } from '../../config' import { InvestorPositionService } from '../services/investorPositionService' import { getPeriodStart } from '../../helpers/timekeeperService' - -const _ethApi = api as Provider //const networkPromise = typeof ethApi.getNetwork === 'function' ? ethApi.getNetwork() : null export const handleEvmDeployTranche = errorHandler(_handleEvmDeployTranche) @@ -122,7 +119,12 @@ async function _handleEvmTransfer(event: TransferLog): Promise { const investLpCollect = InvestorTransactionService.collectLpInvestOrder({ ...orderData, address: toAccount!.id }) await investLpCollect.save() - const trancheBalance = await TrancheBalanceService.getOrInit(toAccount!.id, orderData.poolId, orderData.trancheId) + const trancheBalance = await TrancheBalanceService.getOrInit( + toAccount!.id, + orderData.poolId, + orderData.trancheId, + timestamp + ) await trancheBalance.investCollect(orderData.amount) await trancheBalance.save() } diff --git a/src/mappings/handlers/investmentsHandlers.ts b/src/mappings/handlers/investmentsHandlers.ts index 7e1a37b..66d6e27 100644 --- a/src/mappings/handlers/investmentsHandlers.ts +++ b/src/mappings/handlers/investmentsHandlers.ts @@ -74,7 +74,12 @@ async function _handleInvestOrderUpdated(event: SubstrateEvent 0) { const it = InvestorTransactionService.collectInvestOrder(orderData) @@ -248,7 +263,12 @@ async function _handleRedeemOrdersCollected(event: SubstrateEvent 0) { const it = InvestorTransactionService.collectRedeemOrder(orderData) diff --git a/src/mappings/handlers/poolsHandlers.ts b/src/mappings/handlers/poolsHandlers.ts index 3ea49ce..9024f4a 100644 --- a/src/mappings/handlers/poolsHandlers.ts +++ b/src/mappings/handlers/poolsHandlers.ts @@ -240,7 +240,8 @@ async function _handleEpochExecuted(event: SubstrateEvent BigInt(0) && epochState.investFulfillmentPercentage! > BigInt(0)) { diff --git a/src/mappings/services/trancheBalanceService.ts b/src/mappings/services/trancheBalanceService.ts index 9cc45fb..5249fad 100644 --- a/src/mappings/services/trancheBalanceService.ts +++ b/src/mappings/services/trancheBalanceService.ts @@ -1,13 +1,14 @@ import { TrancheBalance } from '../../types/models/TrancheBalance' export class TrancheBalanceService extends TrancheBalance { - static init(address: string, poolId: string, trancheId: string) { + static init(address: string, poolId: string, trancheId: string, timestamp: Date) { logger.info(`Initialising new TrancheBalance: ${address}-${poolId}-${trancheId}`) const trancheBalance = new this( `${address}-${poolId}-${trancheId}`, address, poolId, `${poolId}-${trancheId}`, + timestamp, BigInt(0), BigInt(0), BigInt(0), @@ -24,10 +25,10 @@ export class TrancheBalanceService extends TrancheBalance { return trancheBalance as TrancheBalanceService | undefined } - static getOrInit = async (address: string, poolId: string, trancheId: string) => { + static async getOrInit(address: string, poolId: string, trancheId: string, timestamp: Date) { let trancheBalance = await this.getById(address, poolId, trancheId) if (!trancheBalance) { - trancheBalance = this.init(address, poolId, trancheId) + trancheBalance = this.init(address, poolId, trancheId, timestamp) await trancheBalance.save() } return trancheBalance as TrancheBalanceService