Skip to content

Commit 4313584

Browse files
MikeHathawayMike
andauthored
Update info multicall (#958)
* add poolRatesAndFeesMulticall; add Multicall to poolInfoUtils * add poolDetailsMulticall method * add natspec; remove unused functions * update tests * add poolBalanceDetails for remaining subgraph rpc calls * expand poolInfoUtilsMulticall unit tests * normalize token balances in poolBalanceDetails * replace internal multicall usage --------- Co-authored-by: Mike <[email protected]>
1 parent d343cc9 commit 4313584

File tree

3 files changed

+155
-44
lines changed

3 files changed

+155
-44
lines changed

src/PoolInfoUtils.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pragma solidity 0.8.18;
44

55
import { Math } from '@openzeppelin/contracts/utils/math/Math.sol';
6+
import { Multicall } from '@openzeppelin/contracts/utils/Multicall.sol';
67

78
import { IPool, IERC20Token } from './interfaces/pool/IPool.sol';
89

@@ -32,7 +33,7 @@ import { PoolCommons } from './libraries/external/PoolCommons.sol';
3233
* @notice Contract for providing information for any deployed pool.
3334
* @dev Pool info is calculated using same helper functions / logic as in `Pool` contracts.
3435
*/
35-
contract PoolInfoUtils {
36+
contract PoolInfoUtils is Multicall {
3637

3738
/**
3839
* @notice Exposes status of a liquidation auction.

src/PoolInfoUtilsMulticall.sol

Lines changed: 100 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,27 @@
22

33
pragma solidity 0.8.18;
44

5+
import { Multicall } from '@openzeppelin/contracts/utils/Multicall.sol';
6+
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
7+
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
8+
59
import { PoolInfoUtils } from "./PoolInfoUtils.sol";
610

11+
import { IPool } from "./interfaces/pool/IPool.sol";
12+
import { IERC20Pool } from "./interfaces/pool/erc20/IERC20Pool.sol";
13+
714
contract PoolInfoUtilsMulticall {
815

916
PoolInfoUtils public immutable poolInfoUtils;
1017

18+
struct PoolLoansInfo {
19+
uint256 poolSize;
20+
uint256 loansCount;
21+
address maxBorrower;
22+
uint256 pendingInflator;
23+
uint256 pendingInterestFactor;
24+
}
25+
1126
struct PoolPriceInfo {
1227
uint256 hpb;
1328
uint256 hpbIndex;
@@ -17,6 +32,12 @@ contract PoolInfoUtilsMulticall {
1732
uint256 lupIndex;
1833
}
1934

35+
struct PoolRatesAndFees {
36+
uint256 lenderInterestMargin;
37+
uint256 borrowFeeRate;
38+
uint256 depositFeeRate;
39+
}
40+
2041
struct PoolReservesInfo {
2142
uint256 reserves;
2243
uint256 claimableReserves;
@@ -32,38 +53,47 @@ contract PoolInfoUtilsMulticall {
3253
uint256 poolTargetUtilization;
3354
}
3455

35-
struct BucketInfo {
36-
uint256 price;
37-
uint256 quoteTokens;
38-
uint256 collateral;
39-
uint256 bucketLP;
40-
uint256 scale;
41-
uint256 exchangeRate;
56+
struct PoolBalanceDetails {
57+
uint256 debt; // debtInfo()
58+
uint256 accruedDebt; // debtInfo()
59+
uint256 debtInAuction; // debtInfo()
60+
uint256 t0Debt2ToCollateral; // debtInfo()
61+
uint256 depositUpToIndex;
62+
uint256 quoteTokenBalance;
63+
uint256 collateralTokenBalance;
4264
}
4365

4466
constructor(PoolInfoUtils poolInfoUtils_) {
4567
poolInfoUtils = poolInfoUtils_;
4668
}
4769

4870
/**
49-
* @notice Retrieves PoolPriceInfo, PoolReservesInfo, PoolUtilizationInfo and BucketInfo
71+
* @notice Retrieves PoolLoansInfo, PoolPriceInfo, PoolRatesAndFees, PoolReservesInfo and PoolUtilizationInfo
72+
* @dev This function is used to retrieve pool details available from PoolInfoUtils in a single RPC call for Indexers.
5073
* @param ajnaPool_ Address of `Ajna` pool
51-
* @param bucketIndex_ The index of the bucket to retrieve
74+
* @return poolLoansInfo_ Pool loans info struct
5275
* @return poolPriceInfo_ Pool price info struct
76+
* @return poolRatesAndFees_ Pool rates and fees struct
5377
* @return poolReservesInfo_ Pool reserves info struct
5478
* @return poolUtilizationInfo_ Pool utilization info struct
55-
* @return bucketInfo_ Bucket info struct
5679
*/
57-
function poolDetailsAndBucketInfo(address ajnaPool_, uint256 bucketIndex_)
58-
external
59-
view
60-
returns(
61-
PoolPriceInfo memory poolPriceInfo_,
62-
PoolReservesInfo memory poolReservesInfo_,
63-
PoolUtilizationInfo memory poolUtilizationInfo_,
64-
BucketInfo memory bucketInfo_
65-
)
66-
{
80+
function poolDetailsMulticall(address ajnaPool_) external view returns (
81+
PoolLoansInfo memory poolLoansInfo_,
82+
PoolPriceInfo memory poolPriceInfo_,
83+
PoolRatesAndFees memory poolRatesAndFees_,
84+
PoolReservesInfo memory poolReservesInfo_,
85+
PoolUtilizationInfo memory poolUtilizationInfo_
86+
) {
87+
// retrieve loans info
88+
(
89+
poolLoansInfo_.poolSize,
90+
poolLoansInfo_.loansCount,
91+
poolLoansInfo_.maxBorrower,
92+
poolLoansInfo_.pendingInflator,
93+
poolLoansInfo_.pendingInterestFactor
94+
) = poolInfoUtils.poolLoansInfo(ajnaPool_);
95+
96+
// retrieve prices info
6797
(
6898
poolPriceInfo_.hpb,
6999
poolPriceInfo_.hpbIndex,
@@ -73,6 +103,12 @@ contract PoolInfoUtilsMulticall {
73103
poolPriceInfo_.lupIndex
74104
) = poolInfoUtils.poolPricesInfo(ajnaPool_);
75105

106+
// retrieve rates and fees
107+
poolRatesAndFees_.lenderInterestMargin = poolInfoUtils.lenderInterestMargin(ajnaPool_);
108+
poolRatesAndFees_.borrowFeeRate = poolInfoUtils.borrowFeeRate(ajnaPool_);
109+
poolRatesAndFees_.depositFeeRate = poolInfoUtils.unutilizedDepositFeeRate(ajnaPool_);
110+
111+
// retrieve reserves info
76112
(
77113
poolReservesInfo_.reserves,
78114
poolReservesInfo_.claimableReserves,
@@ -81,21 +117,13 @@ contract PoolInfoUtilsMulticall {
81117
poolReservesInfo_.timeRemaining
82118
) = poolInfoUtils.poolReservesInfo(ajnaPool_);
83119

120+
// retrieve utilization info
84121
(
85122
poolUtilizationInfo_.poolMinDebtAmount,
86123
poolUtilizationInfo_.poolCollateralization,
87124
poolUtilizationInfo_.poolActualUtilization,
88125
poolUtilizationInfo_.poolTargetUtilization
89126
) = poolInfoUtils.poolUtilizationInfo(ajnaPool_);
90-
91-
(
92-
bucketInfo_.price,
93-
bucketInfo_.quoteTokens,
94-
bucketInfo_.collateral,
95-
bucketInfo_.bucketLP,
96-
bucketInfo_.scale,
97-
bucketInfo_.exchangeRate
98-
) = poolInfoUtils.bucketInfo(ajnaPool_, bucketIndex_);
99127
}
100128

101129
/**
@@ -105,21 +133,60 @@ contract PoolInfoUtilsMulticall {
105133
* @return borrowFeeRate Borrow fee rate calculated from the pool interest ra
106134
* @return depositFeeRate Deposit fee rate calculated from the pool interest rate
107135
*/
108-
function poolRatesAndFees(address ajnaPool_)
136+
function poolRatesAndFeesMulticall(address ajnaPool_)
109137
external
110-
view
111-
returns
138+
returns
112139
(
113140
uint256 lenderInterestMargin,
114141
uint256 borrowFeeRate,
115142
uint256 depositFeeRate
116-
)
143+
)
117144
{
118145
lenderInterestMargin = poolInfoUtils.lenderInterestMargin(ajnaPool_);
119146
borrowFeeRate = poolInfoUtils.borrowFeeRate(ajnaPool_);
120147
depositFeeRate = poolInfoUtils.unutilizedDepositFeeRate(ajnaPool_);
121148
}
122149

150+
/**
151+
* @notice Retrieves pool debtInfo, depositUpToIndex, quoteTokenBalance and collateralTokenBalance
152+
* @dev This function is used to retrieve pool balance details in a single RPC call for Indexers.
153+
* @param ajnaPool_ Address of `Ajna` pool
154+
* @param index_ Index of deposit
155+
* @param quoteTokenAddress_ Address of quote token
156+
* @param collateralTokenAddress_ Address of collateral token
157+
* @param isNFT_ Boolean indicating if the pool is an NFT pool
158+
* @return poolBalanceDetails_ Pool balance details struct
159+
*/
160+
function poolBalanceDetails(address ajnaPool_, uint256 index_, address quoteTokenAddress_, address collateralTokenAddress_, bool isNFT_)
161+
external view
162+
returns (PoolBalanceDetails memory poolBalanceDetails_)
163+
{
164+
IPool pool = IPool(ajnaPool_);
165+
166+
// pool debtInfo
167+
(poolBalanceDetails_.debt, poolBalanceDetails_.accruedDebt, poolBalanceDetails_.debtInAuction, poolBalanceDetails_.t0Debt2ToCollateral) = pool.debtInfo();
168+
169+
// depositUpToIndex(index_)
170+
poolBalanceDetails_.depositUpToIndex = pool.depositUpToIndex(index_);
171+
172+
// get pool quote token balance
173+
uint256 poolQuoteBalance = IERC20(quoteTokenAddress_).balanceOf(ajnaPool_);
174+
uint256 quoteScale = pool.quoteTokenScale();
175+
// normalize token balance to WAD scale
176+
poolBalanceDetails_.quoteTokenBalance = poolQuoteBalance * quoteScale;
177+
178+
// get pool collateral token balance
179+
if (isNFT_) {
180+
// convert whole NFT amounts to WAD to match pool accounting
181+
poolBalanceDetails_.collateralTokenBalance = IERC721(collateralTokenAddress_).balanceOf(ajnaPool_) * 10**18;
182+
} else {
183+
// normalize token balance to WAD scale
184+
uint256 collateralScale = IERC20Pool(ajnaPool_).collateralScale();
185+
uint256 poolCollateralBalance = IERC20(collateralTokenAddress_).balanceOf(ajnaPool_);
186+
poolBalanceDetails_.collateralTokenBalance = poolCollateralBalance * collateralScale;
187+
}
188+
}
189+
123190
/**
124191
* @notice Aggregate results from multiple read-only function calls
125192
* @param functionSignatures_ Array of signatures of read-only functions to be called

tests/forge/unit/ERC20Pool/ERC20PoolInfoUtils.t.sol

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,6 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
265265
);
266266
}
267267

268-
function testPoolInfoUtilsMulticallPoolAndBucketInfo() external {
269-
PoolInfoUtilsMulticall poolUtilsMulticall = new PoolInfoUtilsMulticall(_poolUtils);
270-
271-
PoolInfoUtilsMulticall.BucketInfo memory bucketInfo;
272-
273-
(,,, bucketInfo) = poolUtilsMulticall.poolDetailsAndBucketInfo(address(_pool), high);
274-
275-
assertEq(bucketInfo.bucketLP, 10_000 * 1e18);
276-
}
277-
278268
function testPoolInfoUtilsMulticall() external {
279269
PoolInfoUtilsMulticall poolUtilsMulticall = new PoolInfoUtilsMulticall(_poolUtils);
280270

@@ -311,4 +301,57 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
311301
assertEq(debt, 21_020.192307692307702000 * 1e18);
312302
assertEq(abi.decode(result[1], (uint256)), _poolUtils.htp(address(_pool)));
313303
}
304+
305+
function testPoolInfoUtilsMulticallRatesAndFees() external {
306+
PoolInfoUtilsMulticall poolUtilsMulticall = new PoolInfoUtilsMulticall(_poolUtils);
307+
308+
(uint256 lim, uint256 bfr, uint256 dfr) = poolUtilsMulticall.poolRatesAndFeesMulticall(address(_pool));
309+
310+
assertGe(lim, 0.000136986301369863 * 1e18);
311+
assertGe(bfr, 0.000961538461538462 * 1e18);
312+
assertGe(dfr, 0.000136986301369863 * 1e18);
313+
}
314+
315+
function testPoolInfoUtilsMulticallPoolDetails() external {
316+
PoolInfoUtilsMulticall poolUtilsMulticall = new PoolInfoUtilsMulticall(_poolUtils);
317+
318+
(
319+
PoolInfoUtilsMulticall.PoolLoansInfo memory poolLoansInfo,
320+
PoolInfoUtilsMulticall.PoolPriceInfo memory poolPriceInfo,
321+
PoolInfoUtilsMulticall.PoolRatesAndFees memory poolRatesAndFees,
322+
PoolInfoUtilsMulticall.PoolReservesInfo memory poolReservesInfo,
323+
PoolInfoUtilsMulticall.PoolUtilizationInfo memory poolUtilizationInfo
324+
) = poolUtilsMulticall.poolDetailsMulticall(address(_pool));
325+
326+
assertEq(poolLoansInfo.poolSize, 50_000 * 1e18);
327+
328+
assertEq(poolPriceInfo.hpb, 3_010.892022197881557845 * 1e18);
329+
assertEq(poolPriceInfo.hpbIndex, 2550);
330+
assertEq(poolPriceInfo.htp, 210.201923076923077020 * 1e18);
331+
assertEq(poolPriceInfo.htpIndex, 3083);
332+
assertEq(poolPriceInfo.lup, 2981.007422784467321543 * 1e18);
333+
assertEq(poolPriceInfo.lupIndex, 2552);
334+
335+
assertGe(poolRatesAndFees.lenderInterestMargin, 0.000136986301369863 * 1e18);
336+
assertGe(poolRatesAndFees.borrowFeeRate, 0.000961538461538462 * 1e18);
337+
assertGe(poolRatesAndFees.depositFeeRate, 0.000136986301369863 * 1e18);
338+
339+
assertEq(poolReservesInfo.reserves, 20.192307692307702000 * 1e18);
340+
341+
assertEq(poolUtilizationInfo.poolMinDebtAmount, 2_102.019230769230770200 * 1e18);
342+
}
343+
344+
function testPoolInfoUtilsMulticallPoolBalanceDetails() external {
345+
PoolInfoUtilsMulticall poolUtilsMulticall = new PoolInfoUtilsMulticall(_poolUtils);
346+
347+
uint256 meaningfulIndex = 5000;
348+
address quoteTokenAddress = IPool(_pool).quoteTokenAddress();
349+
address collateralTokenAddress = IPool(_pool).collateralAddress();
350+
351+
PoolInfoUtilsMulticall.PoolBalanceDetails memory poolBalanceDetails = poolUtilsMulticall.poolBalanceDetails(address(_pool), meaningfulIndex, quoteTokenAddress, collateralTokenAddress, false);
352+
353+
assertEq(poolBalanceDetails.debt, 21_020.192307692307702000 * 1e18);
354+
assertEq(poolBalanceDetails.quoteTokenBalance, 29_000 * 1e18);
355+
assertEq(poolBalanceDetails.collateralTokenBalance, 100 * 1e18);
356+
}
314357
}

0 commit comments

Comments
 (0)