Skip to content

Commit

Permalink
add poolBalanceDetails to updatePool (#78)
Browse files Browse the repository at this point in the history
* add poolBalanceDetails to updatePool

* update tests for usage of poolBalanceDetails

* fix remaining tests

---------

Co-authored-by: Mike <mikehathaway@makerdao.com>
  • Loading branch information
MikeHathaway and Mike committed Oct 30, 2023
1 parent f7e4479 commit 4ded726
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 51 deletions.
76 changes: 76 additions & 0 deletions abis/PoolInfoUtilsMulticall.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,82 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "ajnaPool_",
"type": "address"
},
{
"internalType": "uint256",
"name": "index_",
"type": "uint256"
},
{
"internalType": "address",
"name": "quoteTokenAddress_",
"type": "address"
},
{
"internalType": "address",
"name": "collateralTokenAddress_",
"type": "address"
},
{
"internalType": "bool",
"name": "isNFT_",
"type": "bool"
}
],
"name": "poolBalanceDetails",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "debt",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "accruedDebt",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "debtInAuction",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "t0Debt2ToCollateral",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "depositUpToIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "quoteTokenBalance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "collateralTokenBalance",
"type": "uint256"
}
],
"internalType": "struct PoolInfoUtilsMulticall.PoolBalanceDetails",
"name": "poolBalanceDetails_",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
81 changes: 50 additions & 31 deletions src/utils/pool/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PoolInfoUtils } from '../../../generated/templates/ERC20Pool/PoolInfoUt
import { PoolInfoUtilsMulticall } from '../../../generated/templates/ERC20Pool/PoolInfoUtilsMulticall'

import { MAX_PRICE, MAX_PRICE_INDEX, ONE_BD, poolInfoUtilsAddressTable, poolInfoUtilsMulticallAddressTable, TEN_BI, ZERO_ADDRESS, ZERO_BD, ZERO_BI } from "../constants"
import { addressToBytes, decimalToWad, wadToDecimal } from '../convert'
import { addressToBytes, decimalToWad, wadToDecimal } from '../convert';
import { getTokenBalance } from '../token-erc20'
import { getTokenBalance as getERC721TokenBalance } from '../token-erc721'
import { wmul, wdiv } from '../math'
Expand Down Expand Up @@ -83,17 +83,14 @@ export function getRatesAndFees(poolId: Bytes): RatesAndFees {
}

export function calculateLendRate(
poolAddress: Address,
borrowRate: BigInt,
lenderInterestMargin: BigInt,
poolPricesInfo: PoolPricesInfo,
lenderInterestMargin: BigInt,
meaningfulDeposit: BigInt,
debt: BigInt
): BigDecimal {
const meaningfulPriceIndex = max(poolPricesInfo.lupIndex.toU32(), poolPricesInfo.htpIndex.toU32())
const meaningfulDeposit = depositUpToIndex(poolAddress, meaningfulPriceIndex)
if (meaningfulDeposit.equals(ZERO_BI)) return ZERO_BD
const utilization = wdiv(debt, meaningfulDeposit)
return wadToDecimal(wmul(wmul(borrowRate,lenderInterestMargin), utilization))
return wadToDecimal(wmul(wmul(borrowRate, lenderInterestMargin), utilization))
}

export class LoansInfo {
Expand Down Expand Up @@ -224,10 +221,6 @@ export function updatePool(pool: Pool): void {
pool.maxBorrower = poolLoansInfo.maxBorrower
pool.inflator = wadToDecimal(poolLoansInfo.pendingInflator)

// update amount of debt in pool
const debtInfo = isERC20Pool(pool) ? getDebtInfo(pool) : getDebtInfoERC721Pool(pool)
pool.t0debt = wadToDecimal(wdiv(debtInfo.pendingDebt, poolLoansInfo.pendingInflator))

// update pool prices information
const poolPricesInfo = poolDetails.poolPricesInfo
pool.hpb = wadToDecimal(poolPricesInfo.hpb)
Expand All @@ -250,32 +243,22 @@ export function updatePool(pool: Pool): void {
pool.targetUtilization = wadToDecimal(poolUtilizationInfo.targetUtilization)

// update pool token balances
// update quote token balances, this is common between all pool types
const poolAddress = Address.fromBytes(pool.id)
let token = Token.load(pool.quoteToken)!
let scaleFactor = TEN_BI.pow(18 - token.decimals as u8)
let unnormalizedTokenBalance = getTokenBalance(Address.fromBytes(pool.quoteToken), poolAddress)
pool.quoteTokenBalance = wadToDecimal(unnormalizedTokenBalance.times(scaleFactor))
// update collateral token balances
// use the appropriate contract for querying balanceOf the pool
if (pool.poolType == 'Fungible') {
token = Token.load(pool.collateralToken)!
scaleFactor = TEN_BI.pow(18 - token.decimals as u8)
unnormalizedTokenBalance = getTokenBalance(Address.fromBytes(pool.collateralToken), poolAddress)
} else {
scaleFactor = TEN_BI.pow(18) // assume 18 decimal factor for ERC721
unnormalizedTokenBalance = getERC721TokenBalance(Address.fromBytes(pool.collateralToken), poolAddress)
}
pool.collateralBalance = wadToDecimal(unnormalizedTokenBalance.times(scaleFactor))
const meaningfulPriceIndex = max(poolPricesInfo.lupIndex.toU32(), poolPricesInfo.htpIndex.toU32())
const poolBalanceDetails = getPoolBalanceDetails(pool, BigInt.fromI32(meaningfulPriceIndex))
pool.quoteTokenBalance = wadToDecimal(poolBalanceDetails.quoteTokenBalance)
// FIXME: If isNFT then don't convert wadToDecimal?
pool.collateralBalance = wadToDecimal(poolBalanceDetails.collateralTokenBalance)
// FIXME: update t0debt -> need to take into account pending debt?
// update pool debt info
pool.t0debt = wadToDecimal(poolBalanceDetails.debt)

// update rates and fees which change irrespective of borrow rate
const ratesAndFees = poolDetails.ratesAndFeesInfo
pool.lendRate = calculateLendRate(
poolAddress,
decimalToWad(pool.borrowRate),
ratesAndFees.lenderInterestMargin,
poolPricesInfo,
debtInfo.pendingDebt)
poolBalanceDetails.depositUpToIndex,
poolBalanceDetails.debt)
pool.borrowFeeRate = wadToDecimal(ratesAndFees.borrowFeeRate)
pool.depositFeeRate = wadToDecimal(ratesAndFees.depositFeeRate)
}
Expand Down Expand Up @@ -337,6 +320,41 @@ export function getPoolDetailsMulticall(pool: Pool): PoolDetails {
return poolDetails
}

export class PoolBalanceDetails {
debt: BigInt
accruedDebt: BigInt
debtInAuction: BigInt
t0Debt2ToCollateral: BigInt
depositUpToIndex: BigInt
quoteTokenBalance: BigInt
collateralTokenBalance: BigInt
constructor(debt: BigInt, accruedDebt: BigInt, debtInAuction: BigInt, t0Debt2ToCollateral: BigInt, depositUpToIndex: BigInt, quoteTokenBalance: BigInt, collateralTokenBalance: BigInt) {
this.debt = debt
this.accruedDebt = accruedDebt
this.debtInAuction = debtInAuction
this.t0Debt2ToCollateral = t0Debt2ToCollateral
this.depositUpToIndex = depositUpToIndex
this.quoteTokenBalance = quoteTokenBalance
this.collateralTokenBalance = collateralTokenBalance
}
}
export function getPoolBalanceDetails(pool: Pool, meaningFulIndex: BigInt): PoolBalanceDetails {
const poolInfoUtilsMulticallAddress = poolInfoUtilsMulticallAddressTable.get(dataSource.network())!
const poolInfoUtilsMulticallContract = PoolInfoUtilsMulticall.bind(poolInfoUtilsMulticallAddress)
const poolBalanceDetailsResult = poolInfoUtilsMulticallContract.poolBalanceDetails(Address.fromBytes(pool.id), meaningFulIndex, Address.fromBytes(pool.quoteToken), Address.fromBytes(pool.collateralToken), pool.poolType != 'Fungible')

const poolBalanceDetails = new PoolBalanceDetails(
poolBalanceDetailsResult.debt,
poolBalanceDetailsResult.accruedDebt,
poolBalanceDetailsResult.debtInAuction,
poolBalanceDetailsResult.t0Debt2ToCollateral,
poolBalanceDetailsResult.depositUpToIndex,
poolBalanceDetailsResult.quoteTokenBalance,
poolBalanceDetailsResult.collateralTokenBalance
)
return poolBalanceDetails
}

// TODO: rearrange into organized sections
/****************/
/*** Pointers ***/
Expand Down Expand Up @@ -414,6 +432,7 @@ export function getBurnInfoERC721Pool(pool: Pool, burnEpoch: BigInt): BurnInfo {
return burnInfo
}

// FIXME: this needs to be updated
export class DebtInfo {
pendingDebt: BigInt
accruedDebt: BigInt
Expand Down
31 changes: 27 additions & 4 deletions tests/erc-20-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { mockGetAuctionInfo, mockGetAuctionStatus, mockGetBorrowerInfo, mockGetBucketInfo, mockGetBurnInfo, mockGetCurrentBurnEpoch, mockGetDebtInfo, mockGetLPBValueInQuote, mockGetLenderInfo, mockGetRatesAndFees, mockPoolInfoUtilsPoolUpdateCalls, mockTokenBalance } from "./utils/mock-contract-calls"
import { BucketInfo, getBucketId } from "../src/utils/pool/bucket"
import { addressToBytes, wadToDecimal } from "../src/utils/convert"
import { FIVE_PERCENT_BI, MAX_PRICE, MAX_PRICE_BI, MAX_PRICE_INDEX, ONE_BI, ONE_PERCENT_BI, ONE_WAD_BI, TWO_BI, ZERO_ADDRESS, ZERO_BD, ZERO_BI } from "../src/utils/constants"
import { FIVE_PERCENT_BI, MAX_PRICE, MAX_PRICE_BI, MAX_PRICE_INDEX, ONE_BI, ONE_PERCENT_BI, ONE_WAD_BI, TWO_BI, ZERO_ADDRESS, ZERO_BD, ZERO_BI } from '../src/utils/constants';
import { Account, Lend, Loan, Pool } from "../generated/schema"
import { getLendId } from "../src/utils/pool/lend"
import { BorrowerInfo, getLoanId } from "../src/utils/pool/loan"
Expand Down Expand Up @@ -467,11 +467,34 @@ describe("ERC20Pool assertions", () => {
const amountBorrowed = BigInt.fromString("567529276179422528643") // 567.529276179422528643 * 1e18
const collateralPledged = BigInt.fromI32(1067)
const lup = BigInt.fromString("9529276179422528643") // 9.529276179422528643 * 1e18

const expectedPoolDebtInfo = new DebtInfo(amountBorrowed, ZERO_BI, ZERO_BI, ZERO_BI)
mockGetDebtInfo(poolAddress, expectedPoolDebtInfo)
const index = BigInt.fromI32(234)

const inflator = BigInt.fromString("1002804000000000000")

mockPoolInfoUtilsPoolUpdateCalls(poolAddress, {
poolSize: ZERO_BI,
debt: amountBorrowed,
loansCount: ONE_BI,
maxBorrower: borrower,
inflator: inflator,
hpb: ZERO_BI, //TODO: indexToPrice(price)
hpbIndex: index,
htp: ZERO_BI, //TODO: indexToPrice(price)
htpIndex: ZERO_BI,
lup: lup,
lupIndex: BigInt.fromU32(MAX_PRICE_INDEX),
reserves: ZERO_BI,
claimableReserves: ZERO_BI,
claimableReservesRemaining: ZERO_BI,
reserveAuctionPrice: ZERO_BI,
currentBurnEpoch: BigInt.fromI32(9998102),
reserveAuctionTimeRemaining: ZERO_BI,
minDebtAmount: ZERO_BI,
collateralization: ONE_WAD_BI,
actualUtilization: ZERO_BI,
targetUtilization: ONE_WAD_BI
})

const expectedBorrowerInfo = new BorrowerInfo(
wdiv(amountBorrowed, inflator),
collateralPledged,
Expand Down
30 changes: 26 additions & 4 deletions tests/erc-721-pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,34 @@ describe("Describe entity assertions", () => {
const tokenIdsPledged = [BigInt.fromI32(234), BigInt.fromI32(345)]
const amountPledged = BigInt.fromString("2000000000000000000")
const lup = BigInt.fromString("9529276179422528643") // 9.529276179422528643 * 1e18

// mock required contract calls
const expectedPoolDebtInfo = new DebtInfo(amountBorrowed, ZERO_BI, ZERO_BI, ZERO_BI)
mockGetDebtInfo(poolAddress, expectedPoolDebtInfo)
const index = BigInt.fromI32(123)

const inflator = BigInt.fromString("1002804000000000000")

mockPoolInfoUtilsPoolUpdateCalls(poolAddress, {
poolSize: ZERO_BI,
debt: amountBorrowed,
loansCount: ONE_BI,
maxBorrower: borrower,
inflator: inflator,
hpb: ZERO_BI, //TODO: indexToPrice(price)
hpbIndex: index,
htp: ZERO_BI, //TODO: indexToPrice(price)
htpIndex: ZERO_BI,
lup: lup,
lupIndex: BigInt.fromU32(MAX_PRICE_INDEX),
reserves: ZERO_BI,
claimableReserves: ZERO_BI,
claimableReservesRemaining: ZERO_BI,
reserveAuctionPrice: ZERO_BI,
currentBurnEpoch: BigInt.fromI32(9998102),
reserveAuctionTimeRemaining: ZERO_BI,
minDebtAmount: ZERO_BI,
collateralization: ONE_WAD_BI,
actualUtilization: ZERO_BI,
targetUtilization: ONE_WAD_BI
})

const expectedBorrowerInfo = new BorrowerInfo(
wdiv(amountBorrowed, inflator),
amountPledged,
Expand Down
57 changes: 45 additions & 12 deletions tests/utils/mock-contract-calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createMockedFunction } from "matchstick-as"

import { BucketInfo } from "../../src/utils/pool/bucket"
import { positionManagerAddressTable, poolInfoUtilsAddressTable, ZERO_BI, ONE_BI, poolInfoUtilsMulticallAddressTable } from '../../src/utils/constants';
import { BurnInfo, DebtInfo, LoansInfo, PoolPricesInfo, PoolUtilizationInfo, ReservesInfo, PoolDetails, RatesAndFees } from '../../src/utils/pool/pool';
import { BurnInfo, DebtInfo, LoansInfo, PoolPricesInfo, PoolUtilizationInfo, ReservesInfo, PoolDetails, RatesAndFees, PoolBalanceDetails, depositUpToIndex } from '../../src/utils/pool/pool';
import { AuctionInfo, AuctionStatus } from "../../src/utils/pool/liquidation"
import { BorrowerInfo } from "../../src/utils/pool/loan"
import { wdiv, wmin, wmul } from "../../src/utils/math"
Expand Down Expand Up @@ -291,7 +291,6 @@ export function mockGetPoolDetailsMulticall(pool: Address, expectedPoolDetails:

createMockedFunction(poolInfoUtilsMulticallAddressTable.get(dataSource.network())!, 'poolDetailsMulticall', 'poolDetailsMulticall(address):((uint256,uint256,address,uint256,uint256),(uint256,uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256),(uint256,uint256,uint256,uint256,uint256),(uint256,uint256,uint256,uint256))')
.withArgs([ethereum.Value.fromAddress(pool)])
// TODO: create new expecte tuple containings the retrieved params from each struct
.returns([
ethereum.Value.fromTuple(expectedPoolLoansInfo),
ethereum.Value.fromTuple(expectedPoolPricesInfo),
Expand All @@ -301,6 +300,30 @@ export function mockGetPoolDetailsMulticall(pool: Address, expectedPoolDetails:
])
}

// https://github.com/LimeChain/matchstick/issues/376
export function mockGetPoolBalanceDetails(pool: Address, meaningfulIndex: BigInt, quoteToken: Address, collateralToken: Address, isNFT: bool, expectedPoolBalanceDetails: PoolBalanceDetails): void {
const expectedPoolBalanceDetailsTuple = new ethereum.Tuple();
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.debt));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.accruedDebt));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.debtInAuction));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.t0Debt2ToCollateral));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.depositUpToIndex));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.quoteTokenBalance));
expectedPoolBalanceDetailsTuple.push(ethereum.Value.fromUnsignedBigInt(expectedPoolBalanceDetails.collateralTokenBalance));

createMockedFunction(poolInfoUtilsMulticallAddressTable.get(dataSource.network())!, 'poolBalanceDetails', 'poolBalanceDetails(address,uint256,address,address,bool):((uint256,uint256,uint256,uint256,uint256,uint256,uint256))')
.withArgs([
ethereum.Value.fromAddress(pool),
ethereum.Value.fromUnsignedBigInt(meaningfulIndex),
ethereum.Value.fromAddress(quoteToken),
ethereum.Value.fromAddress(collateralToken),
ethereum.Value.fromBoolean(isNFT)
])
.returns([
ethereum.Value.fromTuple(expectedPoolBalanceDetailsTuple)
])
}

// mock auctionInfo contract calls
export function mockGetAuctionInfo(borrower: Address, pool: Address, expectedInfo: AuctionInfo): void {
createMockedFunction(pool, 'auctionInfo', 'auctionInfo(address):(address,uint256,uint256,uint256,uint256,uint256,address,address,address)')
Expand Down Expand Up @@ -383,7 +406,6 @@ export function mockDepositUpToIndex(pool: Address, index: BigInt, expectedInfo:
])
}

// TODO: reuse this for PoolDetails?
export class PoolMockParams {
// loans info mock params
poolSize: BigInt
Expand Down Expand Up @@ -461,17 +483,28 @@ export function mockPoolInfoUtilsPoolUpdateCalls(pool: Address, params: PoolMock
)
mockGetPoolDetailsMulticall(pool, expectedPoolDetails)

const expectedPoolDebtInfo = new DebtInfo(params.debt, ZERO_BI, ZERO_BI, ZERO_BI)
mockGetDebtInfo(pool, expectedPoolDebtInfo)

// TODO: pass expected balance to mock balance calls
// load pool instance
const poolInstance = Pool.load(addressToBytes(pool))!
// mock token balance calls
mockTokenBalance(Address.fromBytes(poolInstance.collateralToken), pool, decimalToWad(poolInstance.collateralBalance))
mockTokenBalance(Address.fromBytes(poolInstance.quoteToken), pool, decimalToWad(poolInstance.quoteTokenBalance))

mockDepositUpToIndex(pool, params.lupIndex, wmul(params.poolSize, params.actualUtilization))
const depositUpToIndex = wmul(params.poolSize, params.actualUtilization)
const meaningfulIndex = BigInt.fromI32(max(params.lupIndex.toI32(), params.htpIndex.toI32()))
const isNFT = poolInstance.poolType != 'Fungible'

mockGetPoolBalanceDetails(
pool,
meaningfulIndex,
Address.fromBytes(poolInstance.quoteToken),
Address.fromBytes(poolInstance.collateralToken),
isNFT,
new PoolBalanceDetails(
params.debt,
ZERO_BI,
ZERO_BI,
ZERO_BI,
depositUpToIndex,
decimalToWad(poolInstance.quoteTokenBalance),
decimalToWad(poolInstance.collateralBalance)
)
)
}

/****************************/
Expand Down

0 comments on commit 4ded726

Please sign in to comment.