Skip to content

Commit e558db4

Browse files
authored
Merge pull request #251 from aave/fix/249-markets-with-more-assets
Fix/249 markets with more assets
2 parents c1ada1c + 68d5980 commit e558db4

File tree

4 files changed

+204
-17
lines changed

4 files changed

+204
-17
lines changed

contracts/protocol/lendingpool/LendingPool.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
8787
_addressesProvider = provider;
8888
_maxStableRateBorrowSizePercent = 2500;
8989
_flashLoanPremiumTotal = 9;
90-
_maxNumberOfReserves = 128;
90+
_maxNumberOfReserves = UserConfiguration._maxReserves;
9191
}
9292

9393
/**
@@ -713,7 +713,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
713713
}
714714

715715
/**
716-
* @dev Returns the fee on flash loans
716+
* @dev Returns the fee on flash loans
717717
*/
718718
function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) {
719719
return _flashLoanPremiumTotal;

contracts/protocol/libraries/configuration/UserConfiguration.sol

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ library UserConfiguration {
1313
uint256 internal constant BORROWING_MASK =
1414
0x5555555555555555555555555555555555555555555555555555555555555555;
1515

16+
uint256 internal constant _maxReserves = 256;
17+
uint256 internal constant _indexCount = _maxReserves / 128 + ((_maxReserves % 128 > 0) ? 1 : 0);
18+
1619
/**
1720
* @dev Sets if the user is borrowing the reserve identified by reserveIndex
1821
* @param self The configuration object
@@ -24,9 +27,11 @@ library UserConfiguration {
2427
uint256 reserveIndex,
2528
bool borrowing
2629
) internal {
27-
require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
28-
self.data =
29-
(self.data & ~(1 << (reserveIndex * 2))) |
30+
require(reserveIndex < _maxReserves, Errors.UL_INVALID_INDEX);
31+
uint256 index = reserveIndex / 128;
32+
reserveIndex = reserveIndex % 128;
33+
self.data[index] =
34+
(self.data[index] & ~(1 << (reserveIndex * 2))) |
3035
(uint256(borrowing ? 1 : 0) << (reserveIndex * 2));
3136
}
3237

@@ -41,9 +46,11 @@ library UserConfiguration {
4146
uint256 reserveIndex,
4247
bool usingAsCollateral
4348
) internal {
44-
require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
45-
self.data =
46-
(self.data & ~(1 << (reserveIndex * 2 + 1))) |
49+
require(reserveIndex < _maxReserves, Errors.UL_INVALID_INDEX);
50+
uint256 index = reserveIndex / 128;
51+
reserveIndex = reserveIndex % 128;
52+
self.data[index] =
53+
(self.data[index] & ~(1 << (reserveIndex * 2 + 1))) |
4754
(uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1));
4855
}
4956

@@ -57,8 +64,10 @@ library UserConfiguration {
5764
DataTypes.UserConfigurationMap memory self,
5865
uint256 reserveIndex
5966
) internal pure returns (bool) {
60-
require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
61-
return (self.data >> (reserveIndex * 2)) & 3 != 0;
67+
require(reserveIndex < _maxReserves, Errors.UL_INVALID_INDEX);
68+
uint256 index = reserveIndex / 128;
69+
reserveIndex = reserveIndex % 128;
70+
return (self.data[index] >> (reserveIndex * 2)) & 3 != 0;
6271
}
6372

6473
/**
@@ -72,8 +81,10 @@ library UserConfiguration {
7281
pure
7382
returns (bool)
7483
{
75-
require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
76-
return (self.data >> (reserveIndex * 2)) & 1 != 0;
84+
require(reserveIndex < _maxReserves, Errors.UL_INVALID_INDEX);
85+
uint256 index = reserveIndex / 128;
86+
reserveIndex = reserveIndex % 128;
87+
return (self.data[index] >> (reserveIndex * 2)) & 1 != 0;
7788
}
7889

7990
/**
@@ -87,8 +98,10 @@ library UserConfiguration {
8798
pure
8899
returns (bool)
89100
{
90-
require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
91-
return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0;
101+
require(reserveIndex < _maxReserves, Errors.UL_INVALID_INDEX);
102+
uint256 index = reserveIndex / 128;
103+
reserveIndex = reserveIndex % 128;
104+
return (self.data[index] >> (reserveIndex * 2 + 1)) & 1 != 0;
92105
}
93106

94107
/**
@@ -97,7 +110,12 @@ library UserConfiguration {
97110
* @return True if the user has been borrowing any reserve, false otherwise
98111
**/
99112
function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
100-
return self.data & BORROWING_MASK != 0;
113+
for (uint8 i = 0; i < _indexCount; i++) {
114+
if (self.data[i] & BORROWING_MASK != 0) {
115+
return true;
116+
}
117+
}
118+
return false;
101119
}
102120

103121
/**
@@ -106,6 +124,11 @@ library UserConfiguration {
106124
* @return True if the user has been borrowing any reserve, false otherwise
107125
**/
108126
function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
109-
return self.data == 0;
127+
for (uint8 i = 0; i < _indexCount; i++) {
128+
if (self.data[i] != 0) {
129+
return false;
130+
}
131+
}
132+
return true;
110133
}
111134
}

contracts/protocol/libraries/types/DataTypes.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ library DataTypes {
4242
}
4343

4444
struct UserConfigurationMap {
45-
uint256 data;
45+
uint256[2] data; // size is _maxReserves / 128 + ((_maxReserves % 128 > 0) ? 1 : 0), but need to be literal
4646
}
4747

4848
enum InterestRateMode {NONE, STABLE, VARIABLE}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { TestEnv, makeSuite } from './helpers/make-suite';
2+
import {
3+
APPROVAL_AMOUNT_LENDING_POOL,
4+
MAX_UINT_AMOUNT,
5+
RAY,
6+
ZERO_ADDRESS,
7+
} from '../../helpers/constants';
8+
import {
9+
convertToCurrencyDecimals,
10+
getContractAddressWithJsonFallback,
11+
} from '../../helpers/contracts-helpers';
12+
import { eContractid, ProtocolErrors, RateMode } from '../../helpers/types';
13+
import { strategyWETH } from '../../markets/aave/reservesConfigs';
14+
import {
15+
deployATokenImplementations,
16+
deployMintableERC20,
17+
deployValidationLogic,
18+
} from '../../helpers/contracts-deployments';
19+
import { getATokenExtraParams } from '../../helpers/init-helpers';
20+
import { ConfigNames } from '../../helpers/configuration';
21+
import { zeroAddress } from 'hardhat/node_modules/ethereumjs-util';
22+
import AaveConfig from '../../markets/aave';
23+
import { getATokensAndRatesHelper } from '../../helpers/contracts-getters';
24+
import { config } from 'process';
25+
import { parseEther } from '@ethersproject/units';
26+
import exp from 'constants';
27+
28+
const { expect } = require('chai');
29+
30+
makeSuite('Adding > 128 asset to many-asset configured pool', (testEnv: TestEnv) => {
31+
const tokens = {};
32+
33+
before('setup', async () => {
34+
const { pool, dai, configurator, deployer, oracle } = testEnv;
35+
const atokenAndRatesDeployer = await getATokensAndRatesHelper();
36+
37+
const reservesCount = await pool.getReservesList();
38+
39+
const daiData = await pool.getReserveData(dai.address);
40+
const poolName = ConfigNames.Aave;
41+
42+
const {
43+
ATokenNamePrefix,
44+
StableDebtTokenNamePrefix,
45+
VariableDebtTokenNamePrefix,
46+
SymbolPrefix,
47+
} = AaveConfig;
48+
49+
// Create tokens
50+
let tokenInitParams = [];
51+
let reserveInitParams = [];
52+
53+
const aTokenImplAddress = await getContractAddressWithJsonFallback(
54+
eContractid.AToken,
55+
poolName
56+
);
57+
const stableDebtImplAddress = await getContractAddressWithJsonFallback(
58+
eContractid.StableDebtToken,
59+
poolName
60+
);
61+
const variableDebtImplAddress = await getContractAddressWithJsonFallback(
62+
eContractid.VariableDebtToken,
63+
poolName
64+
);
65+
66+
const daiPrice = await oracle.getAssetPrice(dai.address);
67+
68+
// All assets will be initialized with the same price as Dai.
69+
for (let i = reservesCount.length; i < 140; i++) {
70+
const tokenName = `RealT-${i}`;
71+
const token = await deployMintableERC20([tokenName, tokenName, '18']);
72+
tokens[tokenName] = token;
73+
74+
tokenInitParams.push({
75+
aTokenImpl: aTokenImplAddress,
76+
stableDebtTokenImpl: stableDebtImplAddress,
77+
variableDebtTokenImpl: variableDebtImplAddress,
78+
underlyingAssetDecimals: 18,
79+
interestRateStrategyAddress: daiData.interestRateStrategyAddress,
80+
underlyingAsset: token.address,
81+
treasury: ZERO_ADDRESS,
82+
incentivesController: ZERO_ADDRESS,
83+
underlyingAssetName: tokenName,
84+
aTokenName: `${ATokenNamePrefix} ${tokenName}`,
85+
aTokenSymbol: `a${SymbolPrefix}${tokenName}`,
86+
variableDebtTokenName: `${VariableDebtTokenNamePrefix} ${SymbolPrefix}${tokenName}`,
87+
variableDebtTokenSymbol: `variableDebt${SymbolPrefix}${tokenName}`,
88+
stableDebtTokenName: `${StableDebtTokenNamePrefix} ${tokenName}`,
89+
stableDebtTokenSymbol: `stableDebt${SymbolPrefix}${tokenName}`,
90+
params: '0x10',
91+
});
92+
93+
reserveInitParams.push({
94+
asset: token.address,
95+
baseLTV: 5000,
96+
liquidationThreshold: 8000,
97+
liquidationBonus: 10500,
98+
reserveFactor: 1000,
99+
stableBorrowingEnabled: false,
100+
borrowingEnabled: true,
101+
});
102+
103+
if (tokenInitParams.length == 2 || i == 139) {
104+
await configurator.batchInitReserve(tokenInitParams);
105+
106+
for (let j = 0; j < reserveInitParams.length; j++) {
107+
let params = reserveInitParams[j];
108+
await configurator
109+
.connect(deployer.signer)
110+
.configureReserveAsCollateral(
111+
params.asset,
112+
params.baseLTV,
113+
params.liquidationThreshold,
114+
params.liquidationBonus
115+
);
116+
await configurator.enableBorrowingOnReserve(params.asset, false);
117+
await oracle.setAssetPrice(params.asset, daiPrice);
118+
}
119+
tokenInitParams = [];
120+
reserveInitParams = [];
121+
}
122+
}
123+
});
124+
125+
it('Check that pool contains >128 assets', async () => {
126+
const { pool } = testEnv;
127+
const reservesCount = await pool.getReservesList();
128+
expect(reservesCount.length).to.be.gt(128);
129+
});
130+
131+
it('Add collateral and borrow asset 130', async () => {
132+
const {
133+
users: [, user, funder],
134+
weth,
135+
pool,
136+
} = testEnv;
137+
138+
const realT130 = tokens[`RealT-${130}`];
139+
140+
const assetdata = await pool.getReserveData(realT130.address);
141+
expect(assetdata.id).to.be.gt(128);
142+
143+
await realT130.connect(funder.signer).mint(parseEther('100'));
144+
await realT130.connect(funder.signer).approve(pool.address, MAX_UINT_AMOUNT);
145+
await pool
146+
.connect(funder.signer)
147+
.deposit(realT130.address, parseEther('100'), funder.address, 0);
148+
149+
await weth.connect(user.signer).mint(parseEther('1'));
150+
await weth.connect(user.signer).approve(pool.address, MAX_UINT_AMOUNT);
151+
await pool.connect(user.signer).deposit(weth.address, parseEther('1'), user.address, 0);
152+
153+
const userDataBefore = await pool.getUserAccountData(user.address);
154+
await pool
155+
.connect(user.signer)
156+
.borrow(realT130.address, parseEther('1'), RateMode.Variable, 0, user.address);
157+
const userDataAfter = await pool.getUserAccountData(user.address);
158+
159+
expect(userDataAfter.totalCollateralETH).to.be.eq(userDataBefore.totalCollateralETH);
160+
expect(userDataBefore.totalDebtETH).to.be.eq(0);
161+
expect(userDataAfter.totalDebtETH).to.be.gt(userDataBefore.totalDebtETH);
162+
expect(userDataAfter.healthFactor).to.be.lt(userDataBefore.healthFactor);
163+
});
164+
});

0 commit comments

Comments
 (0)