Skip to content

Commit

Permalink
Add more LP tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ljiatu committed Jan 1, 2024
1 parent 3a7200e commit e1a9622
Show file tree
Hide file tree
Showing 11 changed files with 614 additions and 430 deletions.
3 changes: 3 additions & 0 deletions contracts/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ contract Errors {
error withdrawTooSoon(); // 0x67982472
error invalidTrancheIndex(); // 0xa82f3ece
error closeTooSoon(); // 0xa38d0553
error alreadyLender();
error notLender();
error nonReinvestYieldLenderCapacityReached();

// credit operation
error creditHasNoCommitment(); // 0xca0cc99a
Expand Down
8 changes: 4 additions & 4 deletions contracts/PoolConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ contract PoolConfig is AccessControl, Initializable {
feeManager.withdrawEAFee(eaWithdrawable);
}

// Make sure the affiliate first loss cover still meets liquidity requirements with the new EA.
// Make sure the new EA meets the liquidity requirements.
if (IPool(pool).isPoolOn()) {
if (
!IFirstLossCover(_firstLossCovers[AFFILIATE_FIRST_LOSS_COVER_INDEX]).isSufficient()
Expand Down Expand Up @@ -472,11 +472,9 @@ contract PoolConfig is AccessControl, Initializable {

function setPoolSettings(PoolSettings memory settings) external {
_onlyOwnerOrHumaMasterAdmin();
if (settings.maxCreditLine >= 2 ** 96) revert Errors.creditLineTooHigh();
if (settings.advanceRateInBps > 10000) {
revert Errors.invalidBasisPointHigherThan10000();
}
// note: this rate can be over 10000 when it requires more backing than the credit limit
_poolSettings = settings;
emit PoolSettingsChanged(
settings.maxCreditLine,
Expand Down Expand Up @@ -556,7 +554,9 @@ contract PoolConfig is AccessControl, Initializable {
function checkLiquidityRequirements() public view {
ITrancheVaultLike juniorTrancheVault = ITrancheVaultLike(juniorTranche);
checkLiquidityRequirementForPoolOwner(juniorTrancheVault.totalAssetsOf(poolOwnerTreasury));
checkLiquidityRequirementForEA(juniorTrancheVault.totalAssetsOf(evaluationAgent));
if (evaluationAgent != address(0)) {
checkLiquidityRequirementForEA(juniorTrancheVault.totalAssetsOf(evaluationAgent));
}
}

/**
Expand Down
22 changes: 10 additions & 12 deletions contracts/PoolFeeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {Errors} from "./Errors.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "hardhat/console.sol";

contract PoolFeeManager is PoolConfigCache, IPoolFeeManager {
using SafeERC20 for IERC20;

Expand Down Expand Up @@ -50,24 +52,24 @@ contract PoolFeeManager is PoolConfigCache, IPoolFeeManager {
function _updatePoolConfigData(PoolConfig _poolConfig) internal virtual override {
address oldUnderlyingToken = address(underlyingToken);
address newUnderlyingToken = _poolConfig.underlyingToken();
if (newUnderlyingToken == address(0)) revert Errors.zeroAddressProvided();
assert(newUnderlyingToken != address(0));
underlyingToken = IERC20(newUnderlyingToken);

address addr = _poolConfig.poolSafe();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
poolSafe = IPoolSafe(addr);

addr = _poolConfig.pool();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
pool = IPool(addr);

addr = address(_poolConfig.humaConfig());
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
humaConfig = HumaConfig(addr);

address oldFirstLossCover = address(firstLossCover);
addr = _poolConfig.getFirstLossCover(AFFILIATE_FIRST_LOSS_COVER_INDEX);
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
firstLossCover = IFirstLossCover(addr);
_resetFirstLossCoverAllowance(
oldFirstLossCover,
Expand Down Expand Up @@ -184,17 +186,13 @@ contract PoolFeeManager is PoolConfigCache, IPoolFeeManager {
AccruedIncomes memory incomes = _accruedIncomes;

uint256 protocolWithdrawn = protocolIncomeWithdrawn;
protocolWithdrawable = incomes.protocolIncome < protocolWithdrawn
? 0
: incomes.protocolIncome - protocolWithdrawn;
protocolWithdrawable = incomes.protocolIncome - protocolWithdrawn;

uint256 poolOwnerWithdrawn = poolOwnerIncomeWithdrawn;
poolOwnerWithdrawable = incomes.poolOwnerIncome < poolOwnerWithdrawn
? 0
: incomes.poolOwnerIncome - poolOwnerWithdrawn;
poolOwnerWithdrawable = incomes.poolOwnerIncome - poolOwnerWithdrawn;

uint256 eaWithdrawn = eaIncomeWithdrawn;
eaWithdrawable = incomes.eaIncome < eaWithdrawn ? 0 : incomes.eaIncome - eaWithdrawn;
eaWithdrawable = incomes.eaIncome - eaWithdrawn;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions contracts/PoolSafe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ contract PoolSafe is PoolConfigCache, IPoolSafe {

function _updatePoolConfigData(PoolConfig _poolConfig) internal virtual override {
address addr = _poolConfig.underlyingToken();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
underlyingToken = IERC20(addr);

addr = _poolConfig.poolFeeManager();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
poolFeeManager = IPoolFeeManager(addr);

addr = _poolConfig.pool();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
pool = IPool(addr);
}

Expand Down
22 changes: 10 additions & 12 deletions contracts/TrancheVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,24 @@ contract TrancheVault is
*/
function _updatePoolConfigData(PoolConfig _poolConfig) internal virtual override {
address addr = _poolConfig.underlyingToken();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
underlyingToken = IERC20(addr);
_decimals = IERC20MetadataUpgradeable(addr).decimals();

addr = _poolConfig.pool();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
pool = IPool(addr);

addr = _poolConfig.poolSafe();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
poolSafe = IPoolSafe(addr);

addr = _poolConfig.epochManager();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
epochManager = IEpochManager(addr);

addr = _poolConfig.calendar();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
calendar = ICalendar(addr);
}

Expand All @@ -103,7 +103,7 @@ contract TrancheVault is
function addApprovedLender(address lender, bool reinvestYield) external {
poolConfig.onlyPoolOperator(msg.sender);
if (lender == address(0)) revert Errors.zeroAddressProvided();
if (hasRole(LENDER_ROLE, lender)) revert Errors.todo();
if (hasRole(LENDER_ROLE, lender)) revert Errors.alreadyLender();

_grantRole(LENDER_ROLE, lender);
depositRecords[lender] = DepositRecord({
Expand All @@ -113,7 +113,7 @@ contract TrancheVault is
});
if (!reinvestYield) {
if (nonReinvestingLenders.length >= MAX_ALLOWED_NUM_NON_REINVESTING_LENDERS)
revert Errors.todo();
revert Errors.nonReinvestYieldLenderCapacityReached();
nonReinvestingLenders.push(lender);
}
}
Expand All @@ -125,7 +125,7 @@ contract TrancheVault is
function removeApprovedLender(address lender) external {
poolConfig.onlyPoolOperator(msg.sender);
if (lender == address(0)) revert Errors.zeroAddressProvided();
if (!hasRole(LENDER_ROLE, lender)) revert Errors.todo();
if (!hasRole(LENDER_ROLE, lender)) revert Errors.notLender();
_revokeRole(LENDER_ROLE, lender);
if (!depositRecords[lender].reinvestYield) {
_removeLenderFromNonReinvestingLenders(lender);
Expand All @@ -142,9 +142,9 @@ contract TrancheVault is
if (depositRecord.reinvestYield == reinvestYield) revert Errors.todo();
if (!depositRecord.reinvestYield && reinvestYield) {
_removeLenderFromNonReinvestingLenders(lender);
} else if (depositRecord.reinvestYield && !reinvestYield) {
} else {
if (nonReinvestingLenders.length >= MAX_ALLOWED_NUM_NON_REINVESTING_LENDERS)
revert Errors.todo();
revert Errors.nonReinvestYieldLenderCapacityReached();
nonReinvestingLenders.push(lender);
}
depositRecord.reinvestYield = reinvestYield;
Expand Down Expand Up @@ -476,8 +476,6 @@ contract TrancheVault is
return supply == 0 ? _assets : (_assets * supply) / _totalAssets;
}

function _updateUserWithdrawable(address user) internal returns (uint256 withdrawableAmount) {}

function _getLatestLenderRedemptionRecordFor(
address account
) internal view returns (LenderRedemptionRecord memory lenderRedemptionRecord) {
Expand Down
6 changes: 3 additions & 3 deletions contracts/mock/MockPoolCredit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ contract MockPoolCredit is PoolConfigCache {

function _updatePoolConfigData(PoolConfig _poolConfig) internal virtual override {
address addr = _poolConfig.poolSafe();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
poolSafe = IPoolSafe(addr);

addr = _poolConfig.underlyingToken();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
IERC20(addr).approve(address(poolSafe), type(uint256).max);

addr = _poolConfig.pool();
if (addr == address(0)) revert Errors.zeroAddressProvided();
assert(addr != address(0));
pool = IPool(addr);
}

Expand Down
49 changes: 27 additions & 22 deletions test/BaseTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,14 @@ export async function setupPoolContracts(
poolOwnerTreasury: SignerWithAddress,
poolOperator: SignerWithAddress,
accounts: SignerWithAddress[],
shouldSetEA: boolean = true,
): Promise<void> {
const poolLiquidityCap = toToken(1_000_000_000);
let settings = await poolConfigContract.getPoolSettings();
const settings = await poolConfigContract.getPoolSettings();
await poolConfigContract
.connect(poolOwner)
.setPoolSettings({ ...settings, ...{ maxCreditLine: toToken(10_000_000) } });
let lpConfig = await poolConfigContract.getLPConfig();
const lpConfig = await poolConfigContract.getLPConfig();
await poolConfigContract
.connect(poolOwner)
.setLPConfig({ ...lpConfig, ...{ liquidityCap: poolLiquidityCap } });
Expand All @@ -386,18 +387,6 @@ export async function setupPoolContracts(
.connect(poolOwner)
.setPoolOwnerTreasury(poolOwnerTreasury.getAddress());

let eaNFTTokenId;
const tx = await eaNFTContract.mintNFT(evaluationAgent.address);
const receipt = await tx.wait();
for (const evt of receipt.events!) {
if (evt.event === "NFTGenerated") {
eaNFTTokenId = evt.args!.tokenId;
}
}
await poolConfigContract
.connect(poolOwner)
.setEvaluationAgent(eaNFTTokenId, evaluationAgent.getAddress());

// Deposit enough liquidity for the pool owner and EA in the junior tranche.
const adminRnR = await poolConfigContract.getAdminRnR();
await mockTokenContract
Expand All @@ -410,17 +399,32 @@ export async function setupPoolContracts(
await juniorTrancheVaultContract
.connect(poolOwnerTreasury)
.makeInitialDeposit(poolOwnerLiquidity);
let expectedInitialLiquidity = poolOwnerLiquidity;

await mockTokenContract
.connect(evaluationAgent)
.approve(poolSafeContract.address, ethers.constants.MaxUint256);
await mockTokenContract.mint(evaluationAgent.getAddress(), toToken(1_000_000_000));
const evaluationAgentLiquidity = BN.from(adminRnR.liquidityRateInBpsByEA)
.mul(poolLiquidityCap)
.div(CONSTANTS.BP_FACTOR);
await juniorTrancheVaultContract
.connect(evaluationAgent)
.makeInitialDeposit(evaluationAgentLiquidity);
if (shouldSetEA) {
let eaNFTTokenId;
const tx = await eaNFTContract.mintNFT(evaluationAgent.address);
const receipt = await tx.wait();
for (const evt of receipt.events!) {
if (evt.event === "NFTGenerated") {
eaNFTTokenId = evt.args!.tokenId;
}
}
await poolConfigContract
.connect(poolOwner)
.setEvaluationAgent(eaNFTTokenId, evaluationAgent.getAddress());
const evaluationAgentLiquidity = BN.from(adminRnR.liquidityRateInBpsByEA)
.mul(poolLiquidityCap)
.div(CONSTANTS.BP_FACTOR);
await juniorTrancheVaultContract
.connect(evaluationAgent)
.makeInitialDeposit(evaluationAgentLiquidity);
expectedInitialLiquidity = expectedInitialLiquidity.add(evaluationAgentLiquidity);
}

await mockTokenContract
.connect(poolOwnerTreasury)
Expand Down Expand Up @@ -451,7 +455,6 @@ export async function setupPoolContracts(
await poolContract.connect(poolOwner).setReadyForFirstLossCoverWithdrawal(true);

await poolContract.connect(poolOwner).enablePool();
const expectedInitialLiquidity = poolOwnerLiquidity.add(evaluationAgentLiquidity);
expect(await poolContract.totalAssets()).to.equal(expectedInitialLiquidity);
expect(await juniorTrancheVaultContract.totalAssets()).to.equal(expectedInitialLiquidity);
expect(await juniorTrancheVaultContract.totalSupply()).to.equal(expectedInitialLiquidity);
Expand Down Expand Up @@ -488,8 +491,9 @@ export async function deployAndSetupPoolContracts(
poolOwnerTreasury: SignerWithAddress,
poolOperator: SignerWithAddress,
accounts: SignerWithAddress[],
shouldSetEA: boolean = true,
): Promise<PoolContracts> {
let [
const [
poolConfigContract,
poolFeeManagerContract,
poolSafeContract,
Expand Down Expand Up @@ -531,6 +535,7 @@ export async function deployAndSetupPoolContracts(
poolOwnerTreasury,
poolOperator,
accounts,
shouldSetEA,
);

return [
Expand Down
4 changes: 2 additions & 2 deletions test/FirstLossCoverTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,11 @@ describe("FirstLossCover Tests", function () {
assets = toToken(100);
});

it("Should return the number of shares as the amount of assets if the current total supply is 0", async function () {
it("Should return the assets as the number of shares if the current total supply is 0", async function () {
expect(await affiliateFirstLossCoverContract.convertToShares(assets)).to.equal(assets);
});

it("Should return the correct amount of assets otherwise", async function () {
it("Should return the correct number of shares otherwise", async function () {
const depositAmount = toToken(5_000);
await affiliateFirstLossCoverContract
.connect(poolOwner)
Expand Down
Loading

0 comments on commit e1a9622

Please sign in to comment.