diff --git a/contracts/test/Mocks/IMockPSR.sol b/contracts/test/Mocks/IMockPSR.sol new file mode 100644 index 000000000..2cde8cc56 --- /dev/null +++ b/contracts/test/Mocks/IMockPSR.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.13; + +interface IMockProtocolShareReserve { + function updateAssetsState(address comptroller, address asset) external; + + function acceptOwnership() external; + + function releaseFunds(address comptroller, address asset, uint256 amount) external; + + function assetsReserves(address asset) external view returns (uint256); +} diff --git a/contracts/test/Mocks/MockPSR.sol b/contracts/test/Mocks/MockPSR.sol deleted file mode 100644 index 8d47be6f2..000000000 --- a/contracts/test/Mocks/MockPSR.sol +++ /dev/null @@ -1,478 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.13; - -import { SafeERC20Upgradeable, IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; -import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; - -import { MaxLoopsLimitHelper } from "../../MaxLoopsLimitHelper.sol"; - -error InvalidAddress(); -error UnsupportedAsset(); -error InvalidTotalPercentage(); -error InvalidMaxLoopsLimit(); - -interface IIncomeDestination { - function updateAssetsState(address comptroller, address asset) external; -} - -interface IVToken { - function underlying() external view returns (address); -} - -interface PoolRegistryInterface { - /*** get VToken in the Pool for an Asset ***/ - function getVTokenForAsset(address comptroller, address asset) external view returns (address); -} - -interface ComptrollerInterface { - function isComptroller() external view returns (bool); -} - -interface IMockProtocolShareReserve { - /// @notice it represents the type of vToken income - enum IncomeType { - SPREAD, - LIQUIDATION - } - - function updateAssetsState(address comptroller, address asset, IncomeType incomeType) external; -} - -contract MockProtocolShareReserve is - AccessControlledV8, - ReentrancyGuardUpgradeable, - MaxLoopsLimitHelper, - IMockProtocolShareReserve -{ - using SafeERC20Upgradeable for IERC20Upgradeable; - - /// @notice protocol income is categorized into two schemas. - /// The first schema is for spread income - /// The second schema is for liquidation income - enum Schema { - PROTOCOL_RESERVES, - ADDITIONAL_REVENUE - } - - struct DistributionConfig { - Schema schema; - /// @dev percenatge is represented without any scale - uint8 percentage; - address destination; - } - - /// @notice address of core pool comptroller contract - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address public immutable CORE_POOL_COMPTROLLER; - - /// @notice address of WBNB contract - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address public immutable WBNB; - - /// @notice address of vBNB contract - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - address public immutable vBNB; - - /// @notice address of pool registry contract - address public poolRegistry; - - uint8 public constant MAX_PERCENT = 100; - - /// @notice comptroller => asset => schema => balance - mapping(address => mapping(address => mapping(Schema => uint256))) public assetsReserves; - - /// @notice asset => balance - mapping(address => uint256) public totalAssetReserve; - - /// @notice configuration for different income distribution targets - DistributionConfig[] public distributionTargets; - - /// @notice Emitted when pool registry address is updated - event PoolRegistryUpdated(address indexed oldPoolRegistry, address indexed newPoolRegistry); - - /// @notice Event emitted after the updation of the assets reserves. - event AssetsReservesUpdated( - address indexed comptroller, - address indexed asset, - uint256 amount, - IncomeType incomeType, - Schema schema - ); - - /// @notice Event emitted when an asset is released to a target - event AssetReleased( - address indexed destination, - address indexed asset, - Schema schema, - uint256 percent, - uint256 amount - ); - - /// @notice Event emitted when asset reserves state is updated - event ReservesUpdated( - address indexed comptroller, - address indexed asset, - Schema schema, - uint256 oldBalance, - uint256 newBalance - ); - - /// @notice Event emitted when distribution configuration is updated - event DistributionConfigUpdated( - address indexed destination, - uint8 oldPercentage, - uint8 newPercentage, - Schema schema - ); - - /// @notice Event emitted when distribution configuration is added - event DistributionConfigAdded(address indexed destination, uint8 percentage, Schema schema); - - /// @notice Event emitted when distribution configuration is removed - event DistributionConfigRemoved(address indexed destination, uint8 percentage, Schema schema); - - /** - * @dev Constructor to initialize the immutable variables - * @param _corePoolComptroller The address of core pool comptroller - * @param _wbnb The address of WBNB - * @param _vbnb The address of vBNB - */ - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(address _corePoolComptroller, address _wbnb, address _vbnb) { - if (_corePoolComptroller == address(0)) revert InvalidAddress(); - if (_wbnb == address(0)) revert InvalidAddress(); - if (_vbnb == address(0)) revert InvalidAddress(); - - CORE_POOL_COMPTROLLER = _corePoolComptroller; - WBNB = _wbnb; - vBNB = _vbnb; - - // Note that the contract is upgradeable. Use initialize() or reinitializers - // to set the state variables. - _disableInitializers(); - } - - /** - * @dev Initializes the deployer to owner. - * @param _accessControlManager The address of ACM contract - * @param _loopsLimit Limit for the loops in the contract to avoid DOS - */ - function initialize(address _accessControlManager, uint256 _loopsLimit) external initializer { - __AccessControlled_init(_accessControlManager); - __ReentrancyGuard_init(); - _setMaxLoopsLimit(_loopsLimit); - } - - /** - * @dev Pool registry setter. - * @param _poolRegistry Address of the pool registry - */ - function setPoolRegistry(address _poolRegistry) external onlyOwner { - if (_poolRegistry == address(0)) revert InvalidAddress(); - emit PoolRegistryUpdated(poolRegistry, _poolRegistry); - poolRegistry = _poolRegistry; - } - - /** - * @dev Add or update destination targets based on destination address - * @param configs configurations of the destinations. - */ - function addOrUpdateDistributionConfigs(DistributionConfig[] calldata configs) external nonReentrant { - _checkAccessAllowed("addOrUpdateDistributionConfigs(DistributionConfig[])"); - - for (uint256 i = 0; i < configs.length; ) { - DistributionConfig memory _config = configs[i]; - if (_config.destination == address(0)) revert InvalidAddress(); - - bool updated = false; - uint256 distributionTargetsLength = distributionTargets.length; - for (uint256 j = 0; j < distributionTargetsLength; ) { - DistributionConfig storage config = distributionTargets[j]; - - if (_config.schema == config.schema && config.destination == _config.destination) { - emit DistributionConfigUpdated( - _config.destination, - config.percentage, - _config.percentage, - _config.schema - ); - config.percentage = _config.percentage; - updated = true; - break; - } - - unchecked { - ++j; - } - } - - if (!updated) { - distributionTargets.push(_config); - emit DistributionConfigAdded(_config.destination, _config.percentage, _config.schema); - } - - unchecked { - ++i; - } - } - - _ensurePercentages(); - _ensureMaxLoops(distributionTargets.length); - } - - /** - * @dev Remove destionation target if percentage is 0 - * @param schema schema of the configuration - * @param destination destination address of the configuration - */ - function removeDistributionConfig(Schema schema, address destination) external { - _checkAccessAllowed("removeDistributionConfig(Schema,address)"); - - uint256 distributionIndex; - bool found = false; - for (uint256 i = 0; i < distributionTargets.length; ) { - DistributionConfig storage config = distributionTargets[i]; - - if (schema == config.schema && destination == config.destination && config.percentage == 0) { - found = true; - distributionIndex = i; - break; - } - - unchecked { - ++i; - } - } - - if (found) { - emit DistributionConfigRemoved( - distributionTargets[distributionIndex].destination, - distributionTargets[distributionIndex].percentage, - distributionTargets[distributionIndex].schema - ); - - distributionTargets[distributionIndex] = distributionTargets[distributionTargets.length - 1]; - distributionTargets.pop(); - } - - _ensurePercentages(); - } - - /** - * @dev Release funds - * @param comptroller the comptroller address of the pool - * @param assets assets to be released to distribution targets - */ - function releaseFunds(address comptroller, address[] calldata assets) external nonReentrant { - for (uint256 i = 0; i < assets.length; ) { - _releaseFund(comptroller, assets[i]); - - unchecked { - ++i; - } - } - } - - /** - * @dev Used to find out the amount of funds that's going to be released when release funds is called. - * @param comptroller the comptroller address of the pool - * @param schema the schema of the distribution target - * @param destination the destination address of the distribution target - * @param asset the asset address which will be released - */ - function getUnreleasedFunds( - address comptroller, - Schema schema, - address destination, - address asset - ) external view returns (uint256) { - uint256 distributionTargetsLength = distributionTargets.length; - for (uint256 i = 0; i < distributionTargetsLength; ) { - DistributionConfig storage _config = distributionTargets[i]; - if (_config.schema == schema && _config.destination == destination) { - uint256 total = assetsReserves[comptroller][asset][schema]; - return (total * _config.percentage) / MAX_PERCENT; - } - - unchecked { - ++i; - } - } - } - - /** - * @dev Returns the total number of distribution targets - */ - function totalDistributions() external view returns (uint256) { - return distributionTargets.length; - } - - /** - * @dev Used to find out the percentage distribution for a particular destination based on schema - * @param destination the destination address of the distribution target - * @param schema the schema of the distribution target - * @return percentage percentage distribution - */ - function getPercentageDistribution(address destination, Schema schema) external view returns (uint256) { - uint256 distributionTargetsLength = distributionTargets.length; - for (uint256 i = 0; i < distributionTargetsLength; ) { - DistributionConfig memory config = distributionTargets[i]; - - if (config.destination == destination && config.schema == schema) { - return config.percentage; - } - - unchecked { - ++i; - } - } - } - - /** - * @dev Update the reserve of the asset for the specific pool after transferring to the protocol share reserve. - * @param comptroller Comptroller address(pool) - * @param asset Asset address. - * @param incomeType type of income - */ - function updateAssetsState( - address comptroller, - address asset, - IncomeType incomeType - ) public override(IMockProtocolShareReserve) nonReentrant { - if (!ComptrollerInterface(comptroller).isComptroller()) revert InvalidAddress(); - if (asset == address(0)) revert InvalidAddress(); - if ( - comptroller != CORE_POOL_COMPTROLLER && - PoolRegistryInterface(poolRegistry).getVTokenForAsset(comptroller, asset) == address(0) - ) revert InvalidAddress(); - - Schema schema = _getSchema(incomeType); - uint256 currentBalance = IERC20Upgradeable(asset).balanceOf(address(this)); - uint256 assetReserve = totalAssetReserve[asset]; - - if (currentBalance > assetReserve) { - uint256 balanceDifference; - unchecked { - balanceDifference = currentBalance - assetReserve; - } - - assetsReserves[comptroller][asset][schema] += balanceDifference; - totalAssetReserve[asset] += balanceDifference; - emit AssetsReservesUpdated(comptroller, asset, balanceDifference, incomeType, schema); - } - } - - /** - * @dev asset from a particular pool to be release to distribution targets - * @param comptroller Comptroller address(pool) - * @param asset Asset address. - */ - function _releaseFund(address comptroller, address asset) internal { - uint256 totalSchemas = uint256(type(Schema).max) + 1; - uint256[] memory schemaBalances = new uint256[](totalSchemas); - uint256 totalBalance; - for (uint256 schemaValue; schemaValue < totalSchemas; ) { - schemaBalances[schemaValue] = assetsReserves[comptroller][asset][Schema(schemaValue)]; - totalBalance += schemaBalances[schemaValue]; - - unchecked { - ++schemaValue; - } - } - - if (totalBalance == 0) { - return; - } - - uint256[] memory totalTransferAmounts = new uint256[](totalSchemas); - for (uint256 i = 0; i < distributionTargets.length; ) { - DistributionConfig memory _config = distributionTargets[i]; - - uint256 transferAmount = (schemaBalances[uint256(_config.schema)] * _config.percentage) / MAX_PERCENT; - totalTransferAmounts[uint256(_config.schema)] += transferAmount; - - if (transferAmount != 0) { - IERC20Upgradeable(asset).safeTransfer(_config.destination, transferAmount); - IIncomeDestination(_config.destination).updateAssetsState(comptroller, asset); - - emit AssetReleased(_config.destination, asset, _config.schema, _config.percentage, transferAmount); - } - - unchecked { - ++i; - } - } - - uint256[] memory newSchemaBalances = new uint256[](totalSchemas); - for (uint256 schemaValue = 0; schemaValue < totalSchemas; ) { - newSchemaBalances[schemaValue] = schemaBalances[schemaValue] - totalTransferAmounts[schemaValue]; - assetsReserves[comptroller][asset][Schema(schemaValue)] = newSchemaBalances[schemaValue]; - totalAssetReserve[asset] = totalAssetReserve[asset] - totalTransferAmounts[schemaValue]; - - emit ReservesUpdated( - comptroller, - asset, - Schema(schemaValue), - schemaBalances[schemaValue], - newSchemaBalances[schemaValue] - ); - - unchecked { - ++schemaValue; - } - } - } - - /** - * @dev Returns the schema based on income type - * @param incomeType type of income - * @return schema schema for distribution - */ - function _getSchema(IncomeType incomeType) internal view returns (Schema schema) { - schema = Schema.ADDITIONAL_REVENUE; - - if (incomeType == IncomeType.SPREAD) { - schema = Schema.PROTOCOL_RESERVES; - } - } - - /** - * @dev This ensures that the total percentage of all the distribution targets is 100% or 0% - */ - function _ensurePercentages() internal view { - uint256 totalSchemas = uint256(type(Schema).max) + 1; - uint8[] memory totalPercentages = new uint8[](totalSchemas); - - uint256 distributionTargetsLength = distributionTargets.length; - for (uint256 i = 0; i < distributionTargetsLength; ) { - DistributionConfig memory config = distributionTargets[i]; - totalPercentages[uint256(config.schema)] += config.percentage; - - unchecked { - ++i; - } - } - for (uint256 schemaValue = 0; schemaValue < totalSchemas; ) { - if (totalPercentages[schemaValue] != MAX_PERCENT && totalPercentages[schemaValue] != 0) - revert InvalidTotalPercentage(); - - unchecked { - ++schemaValue; - } - } - } - - /** - * @dev Returns the underlying asset address for the vToken - * @param vToken vToken address - * @return asset address of asset - */ - function _getUnderlying(address vToken) internal view returns (address) { - if (vToken == vBNB) { - return WBNB; - } else { - return IVToken(vToken).underlying(); - } - } -} diff --git a/tests/hardhat/Fork/RewardsForkTest.ts b/tests/hardhat/Fork/RewardsForkTest.ts index da72449ff..e51060b74 100644 --- a/tests/hardhat/Fork/RewardsForkTest.ts +++ b/tests/hardhat/Fork/RewardsForkTest.ts @@ -35,28 +35,28 @@ const { ADMIN, TOKEN2, VTOKEN2, - TOKEN2_HOLDER, COMPTROLLER, - BLOCK_NUMBER, - REWARD_DISTRIBUTOR1, + TOKEN2_HOLDER, BINANCE_ORACLE, + REWARD_DISTRIBUTOR1, + BLOCK_NUMBER, } = getContractAddresses(network as string); const MANTISSA_ONE = convertToUnit(1, 18); -let impersonatedTimelock: Signer; -let accessControlManager: AccessControlManager; -let comptroller: Comptroller; -let vTOKEN2: VToken; let token2: IERC20; -let rewardDistributor1: RewardsDistributor; +let vTOKEN2: VToken; +let comptroller: Comptroller; let acc1Signer: Signer; let acc2Signer: Signer; let token2Holder: Signer; let comptrollerSigner: Signer; +let impersonatedTimelock: Signer; +let binanceOracle: BinanceOracle; let mintAmount: BigNumberish; let bswBorrowAmount: BigNumberish; -let binanceOracle: BinanceOracle; +let rewardDistributor1: RewardsDistributor; +let accessControlManager: AccessControlManager; async function configureTimelock() { impersonatedTimelock = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); diff --git a/tests/hardhat/Fork/RiskFund.ts b/tests/hardhat/Fork/RiskFund.ts index 8d2d2ff5e..3c82e9811 100644 --- a/tests/hardhat/Fork/RiskFund.ts +++ b/tests/hardhat/Fork/RiskFund.ts @@ -1,1097 +1,270 @@ import { FakeContract, smock } from "@defi-wonderland/smock"; -import { impersonateAccount, loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { expect } from "chai"; -import { BigNumber, constants } from "ethers"; -import { parseUnits } from "ethers/lib/utils"; -import { ethers, upgrades } from "hardhat"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; import { SignerWithAddress } from "hardhat-deploy-ethers/signers"; -import { convertToUnit } from "../../../helpers/utils"; import { AccessControlManager, AccessControlManager__factory, Comptroller, - MockPriceOracle, - MockPriceOracle__factory, - MockToken, - MockToken__factory, + Comptroller__factory, + IERC20, + IERC20__factory, + IMockProtocolShareReserve, + IMockProtocolShareReserve__factory, PancakeRouter, PancakeRouter__factory, - PoolRegistry, ProtocolShareReserve, + ProtocolShareReserve__factory, RiskFund, + RiskFund__factory, Shortfall, + Shortfall__factory, VToken, + VToken__factory, } from "../../../typechain"; -import { deployVTokenBeacon, makeVToken } from "../util/TokenTestHelpers"; -import { getContractAddresses, setForkBlock } from "./utils"; +import { getContractAddresses, initMainnetUser, setForkBlock } from "./utils"; const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; -const { PANCAKE_SWAP_ROUTER, BUSD_HOLDER, USDC_HOLDER, USDT_HOLDER, BLOCK_NUMBER } = getContractAddresses( - network as string, -); - -// Disable a warning about mixing beacons and transparent proxies -upgrades.silenceWarnings(); - -let poolRegistry: PoolRegistry; -let USDC: MockToken; -let BUSD: MockToken; -let USDT: MockToken; -let vUSDC: VToken; -let vUSDT: VToken; -let vUSDC2: VToken; -let vUSDT2: VToken; -let vUSDT3: VToken; -let vBUSD3: VToken; -let priceOracle: MockPriceOracle; -let comptroller1Proxy: Comptroller; -let comptroller2Proxy: Comptroller; -let comptroller3Proxy: Comptroller; -let accessControlManager: AccessControlManager; -let protocolShareReserve: ProtocolShareReserve; -let shortfall: FakeContract; +const { + ACM, + PSR, + ADMIN, + USDT, + VUSDT, + TOKEN1, + TOKEN2, + VTOKEN1, + VTOKEN2, + COMPTROLLER, + RISKFUND, + TREASURY, + SHORTFALL, + USDT_HOLDER, + TOKEN1_HOLDER, + TOKEN2_HOLDER, + SWAP_ROUTER_CORE_POOL, + BLOCK_NUMBER, +} = getContractAddresses(network as string); + +let token1: IERC20; +let token2: IERC20; +let vToken1: VToken; +let vToken2: VToken; +let comptroller: Comptroller; let riskFund: RiskFund; +let shortfall: Shortfall; +let token1Holder: SignerWithAddress; +let token2Holder: SignerWithAddress; +let impersonatedTimelock: SignerWithAddress; +let accessControlManager: AccessControlManager; let pancakeSwapRouter: PancakeRouter | FakeContract; -let busdUser: SignerWithAddress; -let usdcUser: SignerWithAddress; -let usdtUser: SignerWithAddress; -const maxLoopsLimit = 150; -const someNonzeroAddress = "0x0000000000000000000000000000000000000001"; +let protocolShareReserve: ProtocolShareReserve | IMockProtocolShareReserve; +let TOKEN1_ADDRESS: string = TOKEN1; +let VTOKEN1_ADDRESS: string = VTOKEN1; + +let token1Reserve = parseUnits("100", 18); +const token2Reserve = parseUnits("100", 18); +let minAmountOut = parseUnits("10", 18); + +if (network != "sepolia") { + TOKEN1_ADDRESS = USDT; + VTOKEN1_ADDRESS = VUSDT; +} +const configureTimelock = async () => { + impersonatedTimelock = await initMainnetUser(ADMIN, parseEther("2")); +}; const initPancakeSwapRouter = async ( admin: SignerWithAddress, ): Promise> => { let pancakeSwapRouter: PancakeRouter | FakeContract; - if (network == "bsc") { - pancakeSwapRouter = PancakeRouter__factory.connect(PANCAKE_SWAP_ROUTER, admin); + if (network == "bsc" || network == "bsctestnet") { + pancakeSwapRouter = PancakeRouter__factory.connect(SWAP_ROUTER_CORE_POOL, admin); } else { const pancakeSwapRouterFactory = await smock.mock("PancakeRouter"); - pancakeSwapRouter = await pancakeSwapRouterFactory.deploy(PANCAKE_SWAP_ROUTER, admin.address); + pancakeSwapRouter = await pancakeSwapRouterFactory.deploy(SWAP_ROUTER_CORE_POOL, admin.address); await pancakeSwapRouter.deployed(); - const pancakeRouterSigner = await ethers.getSigner(pancakeSwapRouter.address); - // Send some BNB to account so it can faucet money from mock tokens - const tx = await admin.sendTransaction({ - to: pancakeSwapRouter.address, - value: ethers.utils.parseEther("10"), - }); - await tx.wait(); - await USDC.connect(pancakeRouterSigner).faucet(convertToUnit(10000, 18)); - await USDT.connect(pancakeRouterSigner).faucet(convertToUnit(10000, 18)); - await BUSD.connect(pancakeRouterSigner).faucet(convertToUnit(10000, 18)); } - return pancakeSwapRouter; -}; -const initMainnetUser = async (user: string): Promise => { - await impersonateAccount(user); - return ethers.getSigner(user); -}; + await token1.connect(token1Holder).transfer(pancakeSwapRouter.address, parseUnits("1000", 18)); + await token2.connect(token2Holder).transfer(pancakeSwapRouter.address, parseUnits("1000", 18)); -const initMockToken = async (name: string, symbol: string, user: SignerWithAddress): Promise => { - const MockToken = await ethers.getContractFactory("MockToken"); - const token = await MockToken.deploy(name, symbol, 18); - await token.deployed(); - await token.faucet(convertToUnit(10000000, 18)); - await token.transfer(user.address, convertToUnit(10000000, 18)); - return token; + return pancakeSwapRouter; }; const riskFundFixture = async (): Promise => { await setForkBlock(BLOCK_NUMBER); - const [admin, user, ...signers] = await ethers.getSigners(); - - if (network == "bsc") { - busdUser = await initMainnetUser(BUSD_HOLDER); - usdcUser = await initMainnetUser(USDC_HOLDER); - usdtUser = await initMainnetUser(USDT_HOLDER); - - USDC = MockToken__factory.connect(getContractAddresses(network as string).USDC, admin); - BUSD = MockToken__factory.connect(getContractAddresses(network as string).BUSD, admin); - USDT = MockToken__factory.connect(getContractAddresses(network as string).USDT, admin); - } else { - [busdUser, usdcUser, usdtUser] = signers; - - USDC = await initMockToken("Mock USDC", "USDC", usdcUser); - BUSD = await initMockToken("Mock BUSD", "BUSD", busdUser); - USDT = await initMockToken("Mock USDT", "USDT", usdtUser); + await configureTimelock(); + + token2Holder = await initMainnetUser(TOKEN2_HOLDER, ethers.utils.parseUnits("2")); + token1Holder = await initMainnetUser(TOKEN1_HOLDER, ethers.utils.parseUnits("2")); + if (network != "sepolia") token1Holder = await initMainnetUser(USDT_HOLDER, ethers.utils.parseUnits("2")); + + token2 = IERC20__factory.connect(TOKEN2, impersonatedTimelock); + token1 = IERC20__factory.connect(TOKEN1_ADDRESS, impersonatedTimelock); + + pancakeSwapRouter = await initPancakeSwapRouter(impersonatedTimelock); + + accessControlManager = AccessControlManager__factory.connect(ACM, impersonatedTimelock); + await accessControlManager + .connect(impersonatedTimelock) + .giveCallPermission(RISKFUND, "swapPoolsAssets(address[],uint256[],address[][],uint256)", ADMIN); + + riskFund = RiskFund__factory.connect(RISKFUND, impersonatedTimelock); + shortfall = Shortfall__factory.connect(SHORTFALL, impersonatedTimelock); + protocolShareReserve = IMockProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); + + if (network == "bsctestnet") { + protocolShareReserve = ProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); + const distributionConfig1 = { + schema: 0, + percentage: 50, + destination: TREASURY, + }; + + const distributionConfig2 = { + schema: 0, + percentage: 50, + destination: RISKFUND, + }; + + await protocolShareReserve + .connect(impersonatedTimelock) + .addOrUpdateDistributionConfigs([distributionConfig1, distributionConfig2]); + token1Reserve = parseUnits("100", 6); + minAmountOut = parseUnits("10", 6); } - pancakeSwapRouter = await initPancakeSwapRouter(admin); - const AccessControlManagerFactory = await ethers.getContractFactory( - "AccessControlManager", - ); - accessControlManager = await AccessControlManagerFactory.deploy(); - await accessControlManager.deployed(); - - shortfall = await smock.fake("Shortfall"); - await admin.sendTransaction({ - to: shortfall.address, - value: ethers.utils.parseEther("1"), // 1 ether - }); - - const fakeCorePoolComptroller = await smock.fake("Comptroller"); - - const RiskFund = await ethers.getContractFactory("RiskFund"); - riskFund = (await upgrades.deployProxy( - RiskFund, - [pancakeSwapRouter.address, convertToUnit(10, 18), BUSD.address, accessControlManager.address, 150], - { - constructorArgs: [ - fakeCorePoolComptroller.address, - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - }, - )) as RiskFund; - await riskFund.setShortfallContractAddress(shortfall.address); - - const ProtocolShareReserve = await ethers.getContractFactory("ProtocolShareReserve"); - protocolShareReserve = (await upgrades.deployProxy(ProtocolShareReserve, [accessControlManager.address, 10], { - constructorArgs: [ - fakeCorePoolComptroller.address, - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - })) as ProtocolShareReserve; - - const PoolRegistry = await ethers.getContractFactory("PoolRegistry"); - poolRegistry = (await upgrades.deployProxy(PoolRegistry, [accessControlManager.address])) as PoolRegistry; - - await protocolShareReserve.setPoolRegistry(poolRegistry.address); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setCollateralFactor(address,uint256,uint256)", - poolRegistry.address, - ); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setMarketSupplyCaps(address[],uint256[])", - poolRegistry.address, - ); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setMarketBorrowCaps(address[],uint256[])", - poolRegistry.address, - ); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setLiquidationIncentive(uint256)", - poolRegistry.address, - ); + comptroller = Comptroller__factory.connect(COMPTROLLER, impersonatedTimelock); + vToken1 = VToken__factory.connect(VTOKEN1_ADDRESS, impersonatedTimelock); + vToken2 = VToken__factory.connect(VTOKEN2, impersonatedTimelock); - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setMinLiquidatableCollateral(uint256)", - poolRegistry.address, - ); + await vToken1.connect(impersonatedTimelock).setShortfallContract(SHORTFALL); + await vToken1.connect(impersonatedTimelock).setProtocolShareReserve(PSR); - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "supportMarket(address)", - poolRegistry.address, - ); + await vToken2.connect(impersonatedTimelock).setShortfallContract(SHORTFALL); + await vToken2.connect(impersonatedTimelock).setProtocolShareReserve(PSR); - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setCloseFactor(uint256)", - poolRegistry.address, - ); - - await accessControlManager.giveCallPermission( - poolRegistry.address, - "addPool(string,address,uint256,uint256,uint256)", - admin.address, - ); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "setReduceReservesBlockDelta(uint256)", - admin.address, - ); - - await accessControlManager.giveCallPermission(poolRegistry.address, "addMarket(AddMarketInput)", admin.address); - - await accessControlManager.giveCallPermission(riskFund.address, "setMinAmountToConvert(uint256)", admin.address); - - await accessControlManager.giveCallPermission( - riskFund.address, - "swapPoolsAssets(address[],uint256[],address[][],uint256)", - admin.address, - ); - - await accessControlManager.giveCallPermission(riskFund.address, "setConvertibleBaseAsset(address)", admin.address); - - await shortfall.connect(shortfall.wallet).updatePoolRegistry(poolRegistry.address); - - const _closeFactor = convertToUnit(0.05, 18); - const _liquidationIncentive = convertToUnit(1, 18); - const _minLiquidatableCollateral = convertToUnit(100, 18); - - // Deploy Price Oracle - const MockPriceOracle = await ethers.getContractFactory("MockPriceOracle"); - priceOracle = await MockPriceOracle.deploy(); - - const usdtPrice = ".75"; - const usdcPrice = "1"; - const busdPrice = "1.1"; - - await priceOracle.setPrice(USDC.address, convertToUnit(usdcPrice, 18)); - await priceOracle.setPrice(USDT.address, convertToUnit(usdtPrice, 18)); - await priceOracle.setPrice(BUSD.address, convertToUnit(busdPrice, 18)); - - const Comptroller = await ethers.getContractFactory("Comptroller"); - const comptrollerBeacon = await upgrades.deployBeacon(Comptroller, { constructorArgs: [poolRegistry.address] }); - - [comptroller1Proxy, comptroller2Proxy, comptroller3Proxy] = await Promise.all( - [...Array(3)].map(async () => { - const comptroller = (await upgrades.deployBeaconProxy(comptrollerBeacon, Comptroller, [ - maxLoopsLimit, - accessControlManager.address, - ])) as Comptroller; - await comptroller.setPriceOracle(priceOracle.address); - return comptroller; - }), - ); - - // Registering the first pool - await poolRegistry.addPool( - "Pool 1", - comptroller1Proxy.address, - _closeFactor, - _liquidationIncentive, - _minLiquidatableCollateral, - ); - - // Registering the second pool - await poolRegistry.addPool( - "Pool 2", - comptroller2Proxy.address, - _closeFactor, - _liquidationIncentive, - _minLiquidatableCollateral, - ); - - // Registering the third pool - await poolRegistry.addPool( - "Pool 3", - comptroller3Proxy.address, - _closeFactor, - _liquidationIncentive, - _minLiquidatableCollateral, - ); - - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "transferReserveForAuction(address,uint256)", - admin.address, - ); - - const initialSupply = parseUnits("1000", 18); - - // Deploy VTokens - const vTokenBeacon = await deployVTokenBeacon(); - - const commonVTokenParams = { - accessControlManager, - admin, - protocolShareReserve, - beacon: vTokenBeacon, - }; - - const commonMarketParams = { - collateralFactor: parseUnits("0.7", 18), - liquidationThreshold: parseUnits("0.7", 18), - initialSupply, - vTokenReceiver: admin.address, - supplyCap: initialSupply, - borrowCap: initialSupply, - }; - - vUSDT = await makeVToken({ - underlying: USDT, - comptroller: comptroller1Proxy, - decimals: 8, - ...commonVTokenParams, - }); - - await USDT.connect(usdtUser).transfer(admin.address, initialSupply); - await USDT.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vUSDT.address, - ...commonMarketParams, - }); - - vUSDC = await makeVToken({ - underlying: USDC, - comptroller: comptroller1Proxy, - decimals: 18, - ...commonVTokenParams, - }); - - await USDC.connect(usdcUser).transfer(admin.address, initialSupply); - await USDC.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vUSDC.address, - ...commonMarketParams, - }); - - vUSDT2 = await makeVToken({ - underlying: USDT, - comptroller: comptroller2Proxy, - decimals: 8, - ...commonVTokenParams, - }); - - await USDT.connect(usdtUser).transfer(admin.address, initialSupply); - await USDT.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vUSDT2.address, - ...commonMarketParams, - }); - - vUSDC2 = await makeVToken({ - underlying: USDC, - comptroller: comptroller2Proxy, - decimals: 18, - ...commonVTokenParams, - }); - - await USDC.connect(usdcUser).transfer(admin.address, initialSupply); - await USDC.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vUSDC2.address, - ...commonMarketParams, - }); - - vUSDT3 = await makeVToken({ - underlying: USDT, - comptroller: comptroller3Proxy, - decimals: 8, - ...commonVTokenParams, - }); - - await USDT.connect(usdtUser).transfer(admin.address, initialSupply); - await USDT.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vUSDT3.address, - ...commonMarketParams, - }); - - vBUSD3 = await makeVToken({ - underlying: BUSD, - comptroller: comptroller3Proxy, - decimals: 18, - ...commonVTokenParams, - }); - - await BUSD.connect(busdUser).transfer(admin.address, initialSupply); - await BUSD.approve(poolRegistry.address, initialSupply); - await poolRegistry.addMarket({ - vToken: vBUSD3.address, - ...commonMarketParams, - }); - - // Enter Markets - await comptroller1Proxy.connect(user).enterMarkets([vUSDC.address, vUSDT.address]); - await comptroller2Proxy.connect(user).enterMarkets([vUSDC2.address, vUSDT2.address]); - await comptroller3Proxy.connect(user).enterMarkets([vUSDT3.address, vBUSD3.address]); - - await riskFund.setPoolRegistry(poolRegistry.address); + if (network == "sepolia") { + await riskFund.connect(impersonatedTimelock).setPancakeSwapRouter(pancakeSwapRouter.address); + } }; if (FORKING) { describe("Risk Fund Fork: Tests", function () { beforeEach(async function () { await loadFixture(riskFundFixture); - await vUSDC.setReduceReservesBlockDelta(convertToUnit(1, 5)); - await vUSDT.setReduceReservesBlockDelta(convertToUnit(1, 5)); + await token1.connect(token1Holder).approve(vToken1.address, token1Reserve); + await vToken1.connect(token1Holder).addReserves(token1Reserve); - await vUSDC2.setReduceReservesBlockDelta(convertToUnit(1, 5)); + await token2.connect(token2Holder).approve(vToken2.address, token2Reserve); + await vToken2.connect(token2Holder).addReserves(token2Reserve); - await vUSDT2.setReduceReservesBlockDelta(convertToUnit(1, 5)); - - await vUSDT3.setReduceReservesBlockDelta(convertToUnit(1, 5)); - - await vUSDT3.setReduceReservesBlockDelta(convertToUnit(1, 5)); - - const [admin] = await ethers.getSigners(); - const fakeProtocolIncome = await smock.fake("RiskFund"); - - await accessControlManager.giveCallPermission( - protocolShareReserve.address, - "addOrUpdateDistributionConfigs(DistributionConfig[])", - admin.address, - ); - - await protocolShareReserve.connect(admin).addOrUpdateDistributionConfigs([ - { - schema: 0, - percentage: 30, - destination: riskFund.address, - }, - { - schema: 0, - percentage: 70, - destination: fakeProtocolIncome.address, - }, - ]); + await vToken1.reduceReserves(token1Reserve); + await vToken2.reduceReserves(token2Reserve); }); - describe("Test all setters", async function () { - describe("setPoolRegistry", async function () { - it("reverts on invalid PoolRegistry address", async function () { - await expect(riskFund.setPoolRegistry(constants.AddressZero)).to.be.revertedWithCustomError( - riskFund, - "ZeroAddressNotAllowed", - ); - }); - - it("fails if called by a non-owner", async function () { - await expect(riskFund.connect(usdcUser).setPoolRegistry(poolRegistry.address)).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - - it("emits PoolRegistryUpdated event", async function () { - const newPoolRegistry = await smock.fake("PoolRegistry"); - const tx = riskFund.setPoolRegistry(newPoolRegistry.address); - await expect(tx) - .to.emit(riskFund, "PoolRegistryUpdated") - .withArgs(poolRegistry.address, newPoolRegistry.address); - }); - }); - - describe("setShortfallContractAddress", async function () { - it("Reverts on invalid Auction contract address", async function () { - await expect(riskFund.setShortfallContractAddress(constants.AddressZero)).to.be.revertedWithCustomError( - riskFund, - "ZeroAddressNotAllowed", - ); - }); - - it("fails if called by a non-owner", async function () { - await expect(riskFund.connect(usdcUser).setShortfallContractAddress(someNonzeroAddress)).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - - it("emits ShortfallContractUpdated event", async function () { - const newShortfall = await smock.fake("Shortfall"); - const tx = riskFund.setShortfallContractAddress(newShortfall.address); - await expect(tx) - .to.emit(riskFund, "ShortfallContractUpdated") - .withArgs(shortfall.address, newShortfall.address); - }); - }); - - describe("setPancakeSwapRouter", async function () { - it("Reverts on invalid PancakeSwap router contract address", async function () { - await expect(riskFund.setPancakeSwapRouter(constants.AddressZero)).to.be.revertedWithCustomError( - riskFund, - "ZeroAddressNotAllowed", - ); - }); - - it("fails if called by a non-owner", async function () { - await expect(riskFund.connect(usdcUser).setPancakeSwapRouter(someNonzeroAddress)).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - - it("emits PancakeSwapRouterUpdated event", async function () { - const tx = riskFund.setPancakeSwapRouter(someNonzeroAddress); - await expect(tx) - .to.emit(riskFund, "PancakeSwapRouterUpdated") - .withArgs(pancakeSwapRouter.address, someNonzeroAddress); - }); - }); - - describe("swapPoolsAssets", async function () { - let deadline: number; - - beforeEach(async function () { - deadline = (await ethers.provider.getBlock("latest")).timestamp + 100; - }); - - it("fails if called with incorrect arguments", async function () { - await expect( - riskFund.swapPoolsAssets([vUSDT.address, vUSDC.address], [convertToUnit(10, 18)], [], deadline), - ).to.be.revertedWith("Risk fund: markets and amountsOutMin are unequal lengths"); - }); - - it("fails if called with incorrect path length", async function () { - await expect( - riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address], - [convertToUnit(10, 18), convertToUnit(10, 18)], - [], - deadline, - ), - ).to.be.revertedWith("Risk fund: markets and paths are unequal lengths"); - }); - - it("fails if start path is not market", async function () { - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - - await expect( - riskFund.swapPoolsAssets( - [vUSDC.address], - [convertToUnit(10, 18)], - [[USDT.address, BUSD.address]], - deadline, - ), - ).to.be.revertedWith("RiskFund: swap path must start with the underlying asset"); - }); - - it("fails if final path is not base convertible asset", async function () { - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - await expect( - riskFund.swapPoolsAssets( - [vUSDC.address], - [convertToUnit(10, 18)], - [[USDC.address, USDT.address]], - deadline, - ), - ).to.be.revertedWith("RiskFund: finally path must be convertible base asset"); - }); - - it("fails if pool registry is not configured", async function () { - const [admin] = await ethers.getSigners(); - const RiskFund = await ethers.getContractFactory("RiskFund"); - const fakeCorePoolComptroller = await smock.fake("Comptroller"); - const misconfiguredRiskFund = await upgrades.deployProxy( - RiskFund, - [pancakeSwapRouter.address, convertToUnit(10, 18), BUSD.address, accessControlManager.address, 150], - { - constructorArgs: [ - fakeCorePoolComptroller.address, - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - ], - }, - ); - await accessControlManager.giveCallPermission( - misconfiguredRiskFund.address, - "swapPoolsAssets(address[],uint256[],address[][],uint256)", - admin.address, - ); - await expect( - misconfiguredRiskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address], - [convertToUnit(10, 18), convertToUnit(10, 18)], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - ], - deadline, - ), - ).to.be.revertedWithCustomError(misconfiguredRiskFund, "ZeroAddressNotAllowed"); - }); - }); + describe("Assets transfer to riskfund", async function () { + it("Transfer assets from different markets to riskFund", async function () { + let token1ReservesInPSR; + let token2ReservesInPSR; + const token1PreviousBalanceForRiskFund = await token1.balanceOf(riskFund.address); + const token2PreviousBalanceForRiskFund = await token2.balanceOf(riskFund.address); - describe("setMinAmountToConvert", async function () { - it("reverts on invalid min amount to convert", async function () { - await expect(riskFund.setMinAmountToConvert(0)).to.be.revertedWith( - "Risk Fund: Invalid min amount to convert", - ); - }); + const poolAssetReservesPreviousForToken1 = await riskFund.getPoolAssetReserve( + comptroller.address, + token1.address, + ); + const poolAssetReservesPreviousForToken2 = await riskFund.getPoolAssetReserve( + comptroller.address, + token2.address, + ); - it("fails if the call is not allowed by ACM", async function () { - const [admin] = await ethers.getSigners(); - await accessControlManager.revokeCallPermission( - riskFund.address, - "setMinAmountToConvert(uint256)", - admin.address, + if (network == "bsctestnet") { + token1ReservesInPSR = (await protocolShareReserve.assetsReserves(comptroller.address, token1.address, 0)).add( + await protocolShareReserve.assetsReserves(comptroller.address, token1.address, 1), ); - await expect(riskFund.setMinAmountToConvert(1)).to.be.revertedWithCustomError(riskFund, "Unauthorized"); - }); - - it("emits MinAmountToConvertUpdated event", async function () { - const tx = riskFund.setMinAmountToConvert(1); - await expect(tx).to.emit(riskFund, "MinAmountToConvertUpdated").withArgs(convertToUnit(10, 18), 1); - }); - }); - - describe("setConvertibleBaseAsset", async function () { - it("only callable by allowed accounts", async function () { - await expect(riskFund.connect(busdUser).setConvertibleBaseAsset(USDT.address)).to.be.revertedWithCustomError( - riskFund, - "Unauthorized", + token2ReservesInPSR = (await protocolShareReserve.assetsReserves(comptroller.address, token2.address, 0)).add( + await protocolShareReserve.assetsReserves(comptroller.address, token2.address, 1), ); - }); - it("reverts on invalid convertible base asset", async function () { - const [admin] = await ethers.getSigners(); - await expect( - riskFund.connect(admin).setConvertibleBaseAsset("0x0000000000000000000000000000000000000000"), - ).to.be.revertedWith("Risk Fund: new convertible base asset address invalid"); - }); + await protocolShareReserve.releaseFunds(comptroller.address, [token1.address]); + await protocolShareReserve.releaseFunds(comptroller.address, [token2.address]); + } else { + token1ReservesInPSR = await protocolShareReserve.assetsReserves(token1.address); + token2ReservesInPSR = await protocolShareReserve.assetsReserves(token2.address); - it("should emit events on success", async function () { - const [admin] = await ethers.getSigners(); - const tx = riskFund.connect(admin).setConvertibleBaseAsset(USDT.address); - await expect(tx).to.emit(riskFund, "ConvertibleBaseAssetUpdated").withArgs(BUSD.address, USDT.address); - }); - }); - }); + await protocolShareReserve.releaseFunds(comptroller.address, token1.address, token1Reserve); + await protocolShareReserve.releaseFunds(comptroller.address, token2.address, token2Reserve); + } - describe("Risk fund transfers", async function () { - let deadline: number; + const token1NewBalanceForRiskFund = await token1.balanceOf(riskFund.address); + const token2NewBalanceForRiskFund = await token2.balanceOf(riskFund.address); - beforeEach(async function () { - deadline = (await ethers.provider.getBlock("latest")).timestamp + 100; - }); + const poolAssetReservesNewForToken1 = await riskFund.getPoolAssetReserve(comptroller.address, token1.address); + const poolAssetReservesNewForToken2 = await riskFund.getPoolAssetReserve(comptroller.address, token2.address); - it("Checks access control", async function () { - const [admin] = await ethers.getSigners(); - // Revoke - await accessControlManager.revokeCallPermission( - riskFund.address, - "swapPoolsAssets(address[],uint256[],address[][],uint256)", - admin.address, - ); - // Fails - await expect(riskFund.swapPoolsAssets([], [], [], deadline)).to.be.revertedWithCustomError( - riskFund, - "Unauthorized", - ); + expect(token1NewBalanceForRiskFund).to.equal(token1PreviousBalanceForRiskFund.add(token1ReservesInPSR.div(2))); + expect(token2NewBalanceForRiskFund).to.equal(token2PreviousBalanceForRiskFund.add(token2ReservesInPSR.div(2))); - // Reset - await accessControlManager.giveCallPermission( - riskFund.address, - "swapPoolsAssets(address[],uint256[],address[][],uint256)", - admin.address, + expect(poolAssetReservesNewForToken1).to.equal( + poolAssetReservesPreviousForToken1.add(token1ReservesInPSR.div(2)), ); - // Succeeds - await riskFund.swapPoolsAssets([], [], [], deadline); - }); - - it("Convert to BUSD without funds", async function () { - const amount = await riskFund.callStatic.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, + expect(poolAssetReservesNewForToken2).to.equal( + poolAssetReservesPreviousForToken2.add(token2ReservesInPSR.div(2)), ); - expect(amount).equal("0"); }); - it("Below min threshold amount", async function () { - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await vUSDC.reduceReserves(convertToUnit(50, 18)); - - const protocolReserveUSDCBal = await USDC.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDCBal).equal(convertToUnit(50, 18)); - await protocolShareReserve.updateAssetsState(comptroller1Proxy.address, USDC.address, 0); - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - const riskFundUSDCBal = await USDC.balanceOf(riskFund.address); - expect(riskFundUSDCBal).equal(convertToUnit(15, 18)); - - await expect( - riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(9, 18), - convertToUnit(9, 18), - convertToUnit(9, 18), - convertToUnit(9, 18), - convertToUnit(9, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ), - ).to.be.revertedWith("RiskFund: minAmountToConvert violated"); - }); - - it("Above min threshold amount", async function () { - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - - const protocolReserveUSDCBal = await USDC.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDCBal).equal(convertToUnit(100, 18)); - - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - const riskFundUSDCBal = await USDC.balanceOf(riskFund.address); - expect(riskFundUSDCBal).equal(convertToUnit(30, 18)); - - await riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ); - const expectedValue = BigNumber.from("29916047622748892393"); - const actualValue = await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address); - const tolerance = convertToUnit(3, 17); - - const diff = actualValue.sub(expectedValue).abs(); // Calculate the absolute difference - expect(diff.lte(tolerance)).to.be.true; - - const balanceAfter = await USDC.balanceOf(riskFund.address); - expect(balanceAfter).equal("0"); - - const balanceBUSD = await BUSD.balanceOf(riskFund.address); - expect(Number(balanceBUSD)).to.be.closeTo(Number(convertToUnit(30, 18)), Number(convertToUnit(3, 17))); - }); - - it("Add two assets to riskFund", async function () { - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT.address, convertToUnit(1000, 18)); - - await vUSDT.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await vUSDT.reduceReserves(convertToUnit(100, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - - const protocolReserveUSDCBal = await USDC.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDCBal).equal(convertToUnit(100, 18)); - - const protocolReserveUSDTBal = await USDT.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDTBal).equal(convertToUnit(100, 18)); - - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDT.address]); - const riskFundUSDCBal = await USDC.balanceOf(riskFund.address); - expect(riskFundUSDCBal).equal(convertToUnit(30, 18)); - - const riskFundUSDTBal = await USDT.balanceOf(riskFund.address); - expect(riskFundUSDTBal).equal(convertToUnit(30, 18)); - - await riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ); - - const expectedValue = BigNumber.from("59832095245497784786"); - const actualValue = await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address); - const tolerance = convertToUnit(5, 17); - - const diff = actualValue.sub(expectedValue).abs(); // Calculate the absolute difference - expect(diff.lte(tolerance)).to.be.true; - - const balanceBUSD = await BUSD.balanceOf(riskFund.address); - expect(Number(balanceBUSD)).to.be.closeTo(Number(convertToUnit(60, 18)), Number(convertToUnit(3, 17))); - - const pool1Reserve = await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address); - expect(Number(pool1Reserve)).to.be.closeTo(Number(convertToUnit(60, 18)), Number(convertToUnit(3, 17))); + it("Transfer reserves from riskFund to auction contract", async () => { + let auctionTransferAmount = parseUnits("25", 18); + if (network == "bsctestnet") { + await protocolShareReserve.releaseFunds(comptroller.address, [token1.address]); + await protocolShareReserve.releaseFunds(comptroller.address, [token2.address]); + auctionTransferAmount = parseUnits("25", 6); + } else { + await protocolShareReserve.releaseFunds(comptroller.address, token1.address, token1Reserve); + await protocolShareReserve.releaseFunds(comptroller.address, token2.address, token2Reserve); + } + + const shortfallSigner = await initMainnetUser(shortfall.address, parseEther("2")); + await riskFund.connect(shortfallSigner).transferReserveForAuction(comptroller.address, auctionTransferAmount); + + const shortfallBalance = await token1.balanceOf(shortfall.address); + expect(shortfallBalance).to.equal(auctionTransferAmount); }); }); - describe("Transfer to Auction contract", async function () { - let deadline: number; - - beforeEach(async function () { - deadline = (await ethers.provider.getBlock("latest")).timestamp + 100; - }); - - it("Revert while transfering funds to Auction contract", async function () { - await expect( - riskFund.connect(busdUser).transferReserveForAuction(comptroller1Proxy.address, convertToUnit(30, 18)), - ).to.be.revertedWith("Risk fund: Only callable by Shortfall contract"); - - const auctionContract = shortfall.address; - await riskFund.setShortfallContractAddress(auctionContract); - - await expect( - riskFund - .connect(shortfall.wallet) - .transferReserveForAuction(comptroller1Proxy.address, convertToUnit(100, 18)), - ).to.be.revertedWith("Risk Fund: Insufficient pool reserve."); - }); - - it("Transfer funds to auction contact", async function () { - await riskFund.setShortfallContractAddress(shortfall.address); - - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT.address, convertToUnit(1000, 18)); - - await vUSDT.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await vUSDT.reduceReserves(convertToUnit(100, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - const protocolReserveUSDCBal = await USDC.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDCBal).equal(convertToUnit(100, 18)); - - const protocolReserveUSDTBal = await USDT.balanceOf(protocolShareReserve.address); - expect(protocolReserveUSDTBal).equal(convertToUnit(100, 18)); - - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDT.address]); - - await riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ); - - await riskFund - .connect(shortfall.wallet) - .transferReserveForAuction(comptroller1Proxy.address, convertToUnit(20, 18)); - const remainingBalance = await BUSD.balanceOf(riskFund.address); - const poolReserve = await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address); - - expect(remainingBalance).equal(poolReserve); - }); - - it("Should revert the transfer to auction transaction", async function () { - const [admin] = await ethers.getSigners(); - const auctionContract = await smock.fake("Shortfall"); - - await riskFund.setShortfallContractAddress(auctionContract.address); - - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT.address, convertToUnit(1000, 18)); - - await vUSDT.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await vUSDT.reduceReserves(convertToUnit(100, 18)); - await vUSDC.reduceReserves(convertToUnit(100, 18)); - await riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ); - - expect(await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address)).to.be.equal(0); - - // revoke - await accessControlManager.revokeCallPermission( - ethers.constants.AddressZero, - "transferReserveForAuction(address,uint256)", - admin.address, - ); - - await expect( - riskFund.transferReserveForAuction(comptroller1Proxy.address, convertToUnit(20, 18)), - ).to.be.revertedWith("Risk fund: Only callable by Shortfall contract"); - - // reset - await accessControlManager.giveCallPermission( - ethers.constants.AddressZero, - "transferReserveForAuction(address,uint256)", - admin.address, - ); - }); - - it("Transfer single asset from multiple pools to riskFund", async function () { - const auctionContract = await smock.fake("Shortfall"); - - await riskFund.setShortfallContractAddress(auctionContract.address); - - await USDC.connect(usdcUser).approve(vUSDC.address, convertToUnit(1000, 18)); - - await vUSDC.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT.address, convertToUnit(1000, 18)); - - await vUSDT.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await USDC.connect(usdcUser).approve(vUSDC2.address, convertToUnit(1000, 18)); - - await vUSDC2.connect(usdcUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT2.address, convertToUnit(1000, 18)); - - await vUSDT2.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await USDT.connect(usdtUser).approve(vUSDT3.address, convertToUnit(1000, 18)); - - await vUSDT3.connect(usdtUser).addReserves(convertToUnit(200, 18)); - - await BUSD.connect(busdUser).approve(vBUSD3.address, convertToUnit(1000, 18)); - - await vBUSD3.connect(busdUser).addReserves(convertToUnit(50, 18)); - - await vUSDT.reduceReserves(convertToUnit(110, 18)); - await vUSDC.reduceReserves(convertToUnit(120, 18)); - await vUSDT2.reduceReserves(convertToUnit(150, 18)); - await vUSDC2.reduceReserves(convertToUnit(160, 18)); - await vUSDT3.reduceReserves(convertToUnit(175, 18)); - await vBUSD3.reduceReserves(convertToUnit(50, 18)); - - const protocolUSDT = await protocolShareReserve.totalAssetReserve(USDT.address); - const protocolUSDC = await protocolShareReserve.totalAssetReserve(USDC.address); - const protocolBUSD = await protocolShareReserve.totalAssetReserve(BUSD.address); - - expect(protocolUSDT).equal(convertToUnit(435, 18)); - expect(protocolUSDC).equal(convertToUnit(280, 18)); - expect(protocolBUSD).equal(convertToUnit(50, 18)); - - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDT.address]); - - await protocolShareReserve.releaseFunds(comptroller2Proxy.address, [USDT.address]); - - await protocolShareReserve.releaseFunds(comptroller3Proxy.address, [USDT.address]); - - await protocolShareReserve.releaseFunds(comptroller1Proxy.address, [USDC.address]); - - await protocolShareReserve.releaseFunds(comptroller2Proxy.address, [USDC.address]); - - await protocolShareReserve.releaseFunds(comptroller3Proxy.address, [BUSD.address]); - - let riskUSDTFor1 = await riskFund.getPoolAssetReserve(comptroller1Proxy.address, USDT.address); - let riskUSDCFor1 = await riskFund.getPoolAssetReserve(comptroller1Proxy.address, USDC.address); - let riskUSDTFor2 = await riskFund.getPoolAssetReserve(comptroller2Proxy.address, USDT.address); - let riskUSDCFor2 = await riskFund.getPoolAssetReserve(comptroller2Proxy.address, USDC.address); - let riskUSDTFor3 = await riskFund.getPoolAssetReserve(comptroller3Proxy.address, USDT.address); - let riskBUSDTFor3 = await riskFund.getPoolAssetReserve(comptroller3Proxy.address, BUSD.address); - - // 30% transferred to risk fund - expect(riskUSDTFor1).equal(convertToUnit(33, 18)); - expect(riskUSDCFor1).equal(convertToUnit(36, 18)); - expect(riskUSDTFor2).equal(convertToUnit(45, 18)); - expect(riskUSDCFor2).equal(convertToUnit(48, 18)); - expect(riskUSDTFor3).equal(convertToUnit("52.5", 18)); - expect(riskBUSDTFor3).equal(convertToUnit(15, 18)); - - await riskFund.swapPoolsAssets( - [vUSDT.address, vUSDC.address, vUSDT2.address, vUSDC2.address, vUSDT3.address, vBUSD3.address], - [ - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - convertToUnit(10, 18), - ], - [ - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDC.address, BUSD.address], - [USDT.address, BUSD.address], - [USDT.address, BUSD.address], - ], - deadline, - ); - - expect(await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address)).to.be.equal("68803777295611477200"); - - riskUSDTFor1 = await riskFund.getPoolAssetReserve(comptroller1Proxy.address, USDT.address); - riskUSDCFor1 = await riskFund.getPoolAssetReserve(comptroller1Proxy.address, USDC.address); - riskUSDTFor2 = await riskFund.getPoolAssetReserve(comptroller2Proxy.address, USDT.address); - riskUSDCFor2 = await riskFund.getPoolAssetReserve(comptroller2Proxy.address, USDC.address); - riskUSDTFor3 = await riskFund.getPoolAssetReserve(comptroller3Proxy.address, USDT.address); - riskBUSDTFor3 = await riskFund.getPoolAssetReserve(comptroller3Proxy.address, BUSD.address); - - expect(riskUSDTFor1).equal(0); - expect(riskUSDCFor1).equal(0); - expect(riskUSDTFor2).equal(0); - expect(riskUSDCFor2).equal(0); - expect(riskUSDTFor3).equal(0); + describe("Swap Assets", () => { + it("Swap all pool assets and transferring them to auction contract", async () => { + if (network == "bsctestnet") { + await protocolShareReserve.releaseFunds(comptroller.address, [token1.address]); + await protocolShareReserve.releaseFunds(comptroller.address, [token2.address]); + } else { + await protocolShareReserve.releaseFunds(comptroller.address, token1.address, token1Reserve); + await protocolShareReserve.releaseFunds(comptroller.address, token2.address, token2Reserve); + } - // As BUSD is base asset so PoolAssetReserves should be present. - expect(riskBUSDTFor3).to.be.closeTo(convertToUnit(67, 18), convertToUnit(9, 17)); + const riskFundReservesBefore = await riskFund.getPoolsBaseAssetReserves(comptroller.address); + const riskFundBalanceBefore = await token1.balanceOf(riskFund.address); - const poolReserve1 = await riskFund.getPoolsBaseAssetReserves(comptroller1Proxy.address); + const [, amountout] = await pancakeSwapRouter + .connect(impersonatedTimelock) + .getAmountsOut(await token2.balanceOf(riskFund.address), [token2.address, token1.address]); - const poolReserve2 = await riskFund.getPoolsBaseAssetReserves(comptroller2Proxy.address); + const deadline = (await ethers.provider.getBlock("latest")).timestamp + 100; + await riskFund.swapPoolsAssets([vToken2.address], [minAmountOut], [[token2.address, token1.address]], deadline); - const poolReserve3 = await riskFund.getPoolsBaseAssetReserves(comptroller3Proxy.address); + const riskFundReservesAfter = await riskFund.getPoolsBaseAssetReserves(comptroller.address); + const riskFundBalanceAfter = await token1.balanceOf(riskFund.address); - expect(poolReserve1).to.be.closeTo(convertToUnit(68, 18), convertToUnit(9, 17)); - expect(poolReserve2).to.be.closeTo(convertToUnit(92, 18), convertToUnit(9, 17)); - expect(poolReserve3).to.be.closeTo(convertToUnit(67, 18), convertToUnit(9, 17)); + expect(riskFundReservesAfter.sub(riskFundReservesBefore)).to.equal(amountout); + expect(riskFundBalanceAfter.sub(riskFundBalanceBefore)).to.equal(amountout); }); }); }); diff --git a/tests/hardhat/Fork/RiskFundSwap.ts b/tests/hardhat/Fork/RiskFundSwap.ts index fe31ff714..b263c9948 100644 --- a/tests/hardhat/Fork/RiskFundSwap.ts +++ b/tests/hardhat/Fork/RiskFundSwap.ts @@ -8,16 +8,12 @@ import { SignerWithAddress } from "hardhat-deploy-ethers/signers"; import { AccessControlManager, AccessControlManager__factory, - ChainlinkOracle, - ChainlinkOracle__factory, Comptroller, Comptroller__factory, IERC20, IERC20__factory, - MockPriceOracle, - MockPriceOracle__factory, - MockProtocolShareReserve, - MockProtocolShareReserve__factory, + IMockProtocolShareReserve, + IMockProtocolShareReserve__factory, PancakeRouter, PancakeRouter__factory, ProtocolShareReserve, @@ -33,44 +29,43 @@ const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; const { - SWAP_ROUTER_CORE_POOL, - RESILIENT_ORACLE, - CHAINLINK_ORACLE, - COMPTROLLER, + ACM, + PSR, ADMIN, + USDT, TOKEN1, - RISKFUND, TOKEN2, VTOKEN2, + COMPTROLLER, + RISKFUND, + SHORTFALL, + USDT_HOLDER, TOKEN1_HOLDER, TOKEN2_HOLDER, - SHORTFALL, - ACM, - PSR, + SWAP_ROUTER_CORE_POOL, BLOCK_NUMBER, - PANCAKE_SWAP_ROUTER, } = getContractAddresses(network as string); -// // Disable a warning about mixing beacons and transparent proxies -// upgrades.silenceWarnings(); - -let impersonatedTimelock: SignerWithAddress; let token1: IERC20; let token2: IERC20; let vToken2: VToken; -let priceOracle: MockPriceOracle; -let chainlinkOracle: ChainlinkOracle; let comptroller: Comptroller; -let accessControlManager: AccessControlManager; -let protocolShareReserve: ProtocolShareReserve | MockProtocolShareReserve; let riskFund: RiskFund; -let pancakeSwapRouter: PancakeRouter | FakeContract; let token1Holder: SignerWithAddress; let token2Holder: SignerWithAddress; +let impersonatedTimelock: SignerWithAddress; +let accessControlManager: AccessControlManager; +let pancakeSwapRouter: PancakeRouter | FakeContract; +let protocolShareReserve: ProtocolShareReserve | IMockProtocolShareReserve; +let TOKEN1_ADDRESS: string = TOKEN1; const ADD_RESERVE_AMOUNT = parseUnits("100", 18); const REDUCE_RESERVE_AMOUNT = parseUnits("50", 18); +if (network != "sepolia") { + TOKEN1_ADDRESS = USDT; +} + const configureTimelock = async () => { impersonatedTimelock = await initMainnetUser(ADMIN, parseEther("2")); }; @@ -81,16 +76,15 @@ const initPancakeSwapRouter = async ( let pancakeSwapRouter: PancakeRouter | FakeContract; if (network == "bsc" || network == "bsctestnet") { pancakeSwapRouter = PancakeRouter__factory.connect(SWAP_ROUTER_CORE_POOL, admin); - await token1.connect(token1Holder).transfer(SWAP_ROUTER_CORE_POOL, parseUnits("10000", 18)); - await token2.connect(token2Holder).transfer(SWAP_ROUTER_CORE_POOL, parseUnits("10000", 18)); } else { const pancakeSwapRouterFactory = await smock.mock("PancakeRouter"); - pancakeSwapRouter = await pancakeSwapRouterFactory.deploy(PANCAKE_SWAP_ROUTER, admin.address); + pancakeSwapRouter = await pancakeSwapRouterFactory.deploy(SWAP_ROUTER_CORE_POOL, admin.address); await pancakeSwapRouter.deployed(); - console.log(SWAP_ROUTER_CORE_POOL, pancakeSwapRouter.address); - await token1.connect(token1Holder).transfer(pancakeSwapRouter.address, parseUnits("10000", 18)); - await token2.connect(token2Holder).transfer(pancakeSwapRouter.address, parseUnits("10000", 18)); } + + await token1.connect(token1Holder).transfer(pancakeSwapRouter.address, parseUnits("1000", 18)); + await token2.connect(token2Holder).transfer(pancakeSwapRouter.address, parseUnits("1000", 18)); + return pancakeSwapRouter; }; @@ -101,108 +95,72 @@ const riskFundFixture = async (): Promise => { token2Holder = await initMainnetUser(TOKEN2_HOLDER, ethers.utils.parseUnits("2")); token1Holder = await initMainnetUser(TOKEN1_HOLDER, ethers.utils.parseUnits("2")); + if (network != "sepolia") token1Holder = await initMainnetUser(USDT_HOLDER, ethers.utils.parseUnits("2")); + token2 = IERC20__factory.connect(TOKEN2, user); - token1 = IERC20__factory.connect(TOKEN1, user); + token1 = IERC20__factory.connect(TOKEN1_ADDRESS, user); pancakeSwapRouter = await initPancakeSwapRouter(impersonatedTimelock); - console.log(1); accessControlManager = AccessControlManager__factory.connect(ACM, impersonatedTimelock); await accessControlManager .connect(impersonatedTimelock) .giveCallPermission(RISKFUND, "swapPoolsAssets(address[],uint256[],address[][],uint256)", ADMIN); - await accessControlManager.giveCallPermission(RISKFUND, "setConvertibleBaseAsset(address)", ADMIN); riskFund = RiskFund__factory.connect(RISKFUND, impersonatedTimelock); - await riskFund.connect(impersonatedTimelock).setConvertibleBaseAsset(TOKEN1); - - protocolShareReserve = ProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); + protocolShareReserve = IMockProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); if (network == "bsctestnet") { - protocolShareReserve = MockProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); + protocolShareReserve = ProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); } - chainlinkOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedTimelock); - await chainlinkOracle.connect(impersonatedTimelock).setDirectPrice(token1.address, parseUnits("1.1", 18)); - await chainlinkOracle.connect(impersonatedTimelock).setDirectPrice(token2.address, parseUnits(".75", 18)); - - priceOracle = MockPriceOracle__factory.connect(RESILIENT_ORACLE, impersonatedTimelock); - - let tokenConfig = { - asset: token2.address, - oracles: [chainlinkOracle.address, ethers.constants.AddressZero, ethers.constants.AddressZero], - enableFlagsForOracles: [true, false, false], - }; - await priceOracle.connect(impersonatedTimelock).setTokenConfig(tokenConfig); - - tokenConfig = { - asset: token1.address, - oracles: [chainlinkOracle.address, ethers.constants.AddressZero, ethers.constants.AddressZero], - enableFlagsForOracles: [true, false, false], - }; - await priceOracle.connect(impersonatedTimelock).setTokenConfig(tokenConfig); - comptroller = Comptroller__factory.connect(COMPTROLLER, impersonatedTimelock); vToken2 = VToken__factory.connect(VTOKEN2, impersonatedTimelock); await vToken2.connect(impersonatedTimelock).setShortfallContract(SHORTFALL); await vToken2.connect(impersonatedTimelock).setProtocolShareReserve(PSR); - await vToken2.connect(impersonatedTimelock).setProtocolShareReserve(PSR); if (network == "sepolia") { - await protocolShareReserve.connect(impersonatedTimelock).acceptOwnership(); - await riskFund.connect(impersonatedTimelock).acceptOwnership(); await riskFund.connect(impersonatedTimelock).setPancakeSwapRouter(pancakeSwapRouter.address); } }; if (FORKING) { - describe.only("Risk Fund Fork: Swap Tests", () => { + describe("Risk Fund Fork: Swap Tests", () => { + let minAmount = parseUnits("10", 18); + + if (network == "bsctestnet") { + minAmount = parseUnits("10", 6); + } + beforeEach(async () => { await loadFixture(riskFundFixture); - console.log(33); }); it("Swap All Pool Assets", async () => { await token2.connect(token2Holder).approve(vToken2.address, ADD_RESERVE_AMOUNT); - console.log(2); - await vToken2.connect(token2Holder).addReserves(ADD_RESERVE_AMOUNT); - console.log(2); await vToken2.reduceReserves(REDUCE_RESERVE_AMOUNT); - console.log((await token2.balanceOf(riskFund.address)).toString()); - console.log(2); + if (network == "bsctestnet") { await protocolShareReserve.releaseFunds(comptroller.address, [token2.address]); } else { await protocolShareReserve.releaseFunds(comptroller.address, token2.address, REDUCE_RESERVE_AMOUNT); } - console.log((await token2.balanceOf(riskFund.address)).toString()); - console.log(376233); + const riskFundReservesBefore = await riskFund.getPoolsBaseAssetReserves(comptroller.address); + const riskFundBalanceBefore = await token1.balanceOf(riskFund.address); + + const [, amountout] = await pancakeSwapRouter + .connect(impersonatedTimelock) + .getAmountsOut(await token2.balanceOf(riskFund.address), [token2.address, token1.address]); + const deadline = (await ethers.provider.getBlock("latest")).timestamp + 100; - console.log(await riskFund.pancakeSwapRouter()); - // await vToken2.update - // await expect(riskFund.connect(impersonatedTimelock).swapPoolsAssets( - // [vToken2.address], - // [parseUnits("10", 18)], - // [[token2.address, token1.address]], - // deadline, - // )).to.be.revertedWithCustomError(riskFund, "ZeroAddressNotAllowed"); - console.log(await riskFund.poolRegistry()); - await riskFund.swapPoolsAssets( - [vToken2.address], - [parseUnits("10", 18)], - [[token2.address, token1.address]], - deadline, - ); - console.log(376233); - - expect(Number(await riskFund.getPoolsBaseAssetReserves(comptroller.address))).to.be.closeTo( - Number("24931282761361385504"), - Number(parseUnits("1", 18)), - ); - - const balance = await token1.balanceOf(riskFund.address); - expect(Number(balance)).to.be.closeTo(Number(parseUnits("25", 18)), Number(parseUnits("1", 18))); + await riskFund.swapPoolsAssets([vToken2.address], [minAmount], [[token2.address, token1.address]], deadline); + + const riskFundReservesAfter = await riskFund.getPoolsBaseAssetReserves(comptroller.address); + const riskFundBalanceAfter = await token1.balanceOf(riskFund.address); + + expect(riskFundReservesAfter.sub(riskFundReservesBefore)).to.equal(amountout); + expect(riskFundBalanceAfter.sub(riskFundBalanceBefore)).to.equal(amountout); }); }); } diff --git a/tests/hardhat/Fork/Shortfall.ts b/tests/hardhat/Fork/Shortfall.ts index 59d0189e6..985ec276e 100644 --- a/tests/hardhat/Fork/Shortfall.ts +++ b/tests/hardhat/Fork/Shortfall.ts @@ -14,10 +14,10 @@ import { Comptroller__factory, IERC20, IERC20__factory, + IMockProtocolShareReserve, + IMockProtocolShareReserve__factory, MockPriceOracle, MockPriceOracle__factory, - MockProtocolShareReserve, - MockProtocolShareReserve__factory, ProtocolShareReserve, ProtocolShareReserve__factory, RiskFund, @@ -33,14 +33,9 @@ const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; const { - ADMIN, ACM, - RESILIENT_ORACLE, - CHAINLINK_ORACLE, - COMPTROLLER, - BLOCK_NUMBER, - TOKEN1_HOLDER, - TOKEN2_HOLDER, + PSR, + ADMIN, TOKEN1, TOKEN2, VTOKEN1, @@ -48,31 +43,32 @@ const { RISKFUND, SHORTFALL, TREASURY, - PSR, + COMPTROLLER, + TOKEN1_HOLDER, + TOKEN2_HOLDER, + RESILIENT_ORACLE, + CHAINLINK_ORACLE, + BLOCK_NUMBER, } = getContractAddresses(network as string); const { expect } = chai; chai.use(smock.matchers); -let impersonatedTimelock: SignerWithAddress; -let accessControlManager: AccessControlManager; -let resilientOracle: MockPriceOracle; -let chainlinkOracle: ChainlinkOracle; - -let manager: SignerWithAddress; -let user1: SignerWithAddress; -let liquidator: SignerWithAddress; - -let comptroller: Comptroller; -let vTOKEN2: VToken; -let vTOKEN1: VToken; - let token2: IERC20; let token1: IERC20; - +let vTOKEN2: VToken; +let vTOKEN1: VToken; +let comptroller: Comptroller; let shortfall: Shortfall; -let protocolShareReserve: ProtocolShareReserve | MockProtocolShareReserve; let riskFund: RiskFund; +let user1: SignerWithAddress; +let manager: SignerWithAddress; +let liquidator: SignerWithAddress; +let impersonatedTimelock: SignerWithAddress; +let resilientOracle: MockPriceOracle; +let chainlinkOracle: ChainlinkOracle; +let accessControlManager: AccessControlManager; +let protocolShareReserve: ProtocolShareReserve | IMockProtocolShareReserve; const configureTimelock = async () => { impersonatedTimelock = await initMainnetUser(ADMIN, parseEther("2")); @@ -93,13 +89,10 @@ const setupRiskManagementContracts = async () => { riskFund = RiskFund__factory.connect(RISKFUND, impersonatedTimelock); await riskFund.connect(impersonatedTimelock).setConvertibleBaseAsset(TOKEN1); - protocolShareReserve = ProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); - - if (network == "bsctestnet") { - protocolShareReserve = MockProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); - } + protocolShareReserve = IMockProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); if (network == "bsctestnet") { + protocolShareReserve = ProtocolShareReserve__factory.connect(PSR, impersonatedTimelock); const distributionConfig1 = { schema: 0, percentage: 50, @@ -119,12 +112,6 @@ const setupRiskManagementContracts = async () => { shortfall = Shortfall__factory.connect(SHORTFALL, impersonatedTimelock); await shortfall.updateMinimumPoolBadDebt(parseUnits("50", 18)); - - if (network == "sepolia") { - await protocolShareReserve.connect(impersonatedTimelock).acceptOwnership(); - await riskFund.connect(impersonatedTimelock).acceptOwnership(); - await shortfall.connect(impersonatedTimelock).acceptOwnership(); - } }; const setupTokens = async () => { @@ -193,7 +180,6 @@ const pretendRiskFundAccumulatedBaseAsset = async amount => { await token1.approve(vTOKEN1.address, token1Reserve); await vTOKEN1.addReserves(token1Reserve); - await vTOKEN1.reduceReserves(token1Reserve); if (network == "bsctestnet") { @@ -209,7 +195,6 @@ const setup = async () => { await setForkBlock(BLOCK_NUMBER); await configureTimelock(); - await setupRiskManagementContracts(); await setupTokens(); await chainlinkOracle.setDirectPrice(token2.address, parseUnits("100", 18)); diff --git a/tests/hardhat/Fork/borrowAndRepayTest.ts b/tests/hardhat/Fork/borrowAndRepayTest.ts index 1876237a1..57321147c 100644 --- a/tests/hardhat/Fork/borrowAndRepayTest.ts +++ b/tests/hardhat/Fork/borrowAndRepayTest.ts @@ -26,40 +26,42 @@ chai.use(smock.matchers); const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; -console.log(network); + +console.log("fork tests are running on network:", network); + const { - ACM, ACC1, ACC2, ACC3, ADMIN, - COMPTROLLER, - BLOCK_NUMBER, - BINANCE_ORACLE, + ACM, TOKEN1, TOKEN2, VTOKEN1, VTOKEN2, + COMPTROLLER, + BINANCE_ORACLE, TOKEN1_HOLDER, TOKEN2_HOLDER, + BLOCK_NUMBER, } = getContractAddresses(network as string); -let impersonatedTimelock: Signer; -let accessControlManager: AccessControlManager; -let comptroller: Comptroller; -let vTOKEN1: VToken; -let vTOKEN2: VToken; let token1: IERC20; let token2: IERC20; -let priceOracle: ResilientOracleInterface; +let vTOKEN1: VToken; +let vTOKEN2: VToken; +let comptroller: Comptroller; let acc1Signer: Signer; let acc2Signer: Signer; let acc3Signer: Signer; let token2Holder: Signer; let token1Holder: Signer; +let impersonatedTimelock: Signer; let mintAmount: BigNumber; let TOKEN2BorrowAmount: BigNumberish; let binanceOracle: BinanceOracle; +let priceOracle: ResilientOracleInterface; +let accessControlManager: AccessControlManager; async function configureTimelock() { impersonatedTimelock = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); diff --git a/tests/hardhat/Fork/constants.ts b/tests/hardhat/Fork/constants.ts index 4a193ee6d..5f2aeaa6a 100644 --- a/tests/hardhat/Fork/constants.ts +++ b/tests/hardhat/Fork/constants.ts @@ -9,29 +9,27 @@ export const contractAddreseses = { comments: "token2 is HAY token and token1 is DAI token, pancake router and swap router core is picked from bsc networks", ADMIN: "0xcd2a514f04241b7c9A0d5d54441e92E4611929CF", - ADMIN_ORACLE: "0xcd2a514f04241b7c9A0d5d54441e92E4611929CF", ACM: "0x799700ea540f002134C371fB955e2392FD94DbCD", ACC1: "0x3Ac99C7853b58f4AA38b309D372562a5A88bB9C1", ACC2: "0xA4a04C2D661bB514bB8B478CaCB61145894563ef", ACC3: "0x394d1d517e8269596a7E4Cd1DdaC1C928B3bD8b3", - COMPTROLLER: "0xa8cD3a5A542A71D276b35A6AFBb373d37824991a", - TOKEN2: "0xA33524BeFb504152EFBfB4433501D42BCA90e704", TOKEN1: "0x16ffae6A9b4DcDb4a626Ee35721Aa748F0902D9C", - VTOKEN2: "0x5f22a9b867295F4e9fAff66a83a25BdD361807eD", + TOKEN2: "0xA33524BeFb504152EFBfB4433501D42BCA90e704", VTOKEN1: "0x6BF77cE5A5b21fe2175cDeeD728cD5Af5aa53A50", + VTOKEN2: "0x5f22a9b867295F4e9fAff66a83a25BdD361807eD", + COMPTROLLER: "0xa8cD3a5A542A71D276b35A6AFBb373d37824991a", TOKEN2_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", TOKEN1_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", + PSR: "0x4Ef095E9de60DF240af1be963CE568346f9016b8", + SHORTFALL: "0x8b5704cD8A1c877e13cF803386eEaF395DCFfcd4", + RISKFUND: "0x56f32856123a2e094DdBAe19bF56677Bd92C225E", REWARD_DISTRIBUTOR1: "0xf2c98D07e189B67049b8742bDEa42b492a8A96B1", TREASURY: "0xF5b8d3eD8F666956223ea1618B9680416F3dA936", POOL_REGISTRY: "0xe7CE0296D5B0a26fB183ceBf800ECC918eBBD738", CHAINLINK_ORACLE: "0x065a5c9010beD25ddDd45878ada2699959963852", RESILIENT_ORACLE: "0x1B85FAbE5c0846662F5FB0E3598fC48eF587e9f0", - PANCAKE_SWAP_ROUTER: "0x10ED43C718714eb63d5aA57B78B54704E256024E", SWAP_ROUTER_CORE_POOL: "0x83edf1deE1B730b7e8e13C00ba76027D63a51ac0", - PSR: "0x4Ef095E9de60DF240af1be963CE568346f9016b8", - SHORTFALL: "0x8b5704cD8A1c877e13cF803386eEaF395DCFfcd4", - RISKFUND: "0x56f32856123a2e094DdBAe19bF56677Bd92C225E", - BLOCK_NUMBER: 4592930, + BLOCK_NUMBER: 4615016, }, bsctestnet: { comments: "token2 is HAY token and token1 is USDD token", @@ -40,65 +38,55 @@ export const contractAddreseses = { ACC1: "0xe70898180a366F204AA529708fB8f5052ea5723c", ACC2: "0xA4a04C2D661bB514bB8B478CaCB61145894563ef", ACC3: "0x394d1d517e8269596a7E4Cd1DdaC1C928B3bD8b3", - TOKEN2: "0xe73774DfCD551BF75650772dC2cC56a2B6323453", TOKEN1: "0x2E2466e22FcbE0732Be385ee2FBb9C59a1098382", + TOKEN2: "0xe73774DfCD551BF75650772dC2cC56a2B6323453", USDT: "0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c", - COMPTROLLER: "0x10b57706AD2345e590c2eA4DC02faef0d9f5b08B", VTOKEN1: "0x899dDf81DfbbF5889a16D075c352F2b959Dd24A4", - VUSDT: "0x3338988d0beb4419Acb8fE624218754053362D06", VTOKEN2: "0x170d3b2da05cc2124334240fB34ad1359e34C562", + VUSDT: "0x3338988d0beb4419Acb8fE624218754053362D06", + COMPTROLLER: "0x10b57706AD2345e590c2eA4DC02faef0d9f5b08B", + TOKEN1_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", + TOKEN2_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", + USDT_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", + PSR: "0x25c7c7D6Bf710949fD7f03364E9BA19a1b3c10E3", + SHORTFALL: "0x503574a82fE2A9f968d355C8AAc1Ba0481859369", + RISKFUND: "0x487CeF72dacABD7E12e633bb3B63815a386f7012", REWARD_DISTRIBUTOR1: "0xb0269d68CfdCc30Cb7Cd2E0b52b08Fa7Ffd3079b", TREASURY: "0x8b293600C50D6fbdc6Ed4251cc75ECe29880276f", - BINANCE_ORACLE: "0xB58BFDCE610042311Dc0e034a80Cc7776c1D68f5", POOL_REGISTRY: "0xC85491616Fa949E048F3aAc39fbf5b0703800667", SWAP_ROUTER_CORE_POOL: "0x83edf1deE1B730b7e8e13C00ba76027D63a51ac0", RESILIENT_ORACLE: "0x3cD69251D04A28d887Ac14cbe2E14c52F3D57823", CHAINLINK_ORACLE: "0xCeA29f1266e880A1482c06eD656cD08C148BaA32", - PANCAKE_SWAP_ROUTER: "0x10ED43C718714eb63d5aA57B78B54704E256024E", - TOKEN2_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", - TOKEN1_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", - USDT_HOLDER: "0x02EB950C215D12d723b44a18CfF098C6E166C531", - PSR: "0x25c7c7D6Bf710949fD7f03364E9BA19a1b3c10E3", - SHORTFALL: "0x503574a82fE2A9f968d355C8AAc1Ba0481859369", - RISKFUND: "0x487CeF72dacABD7E12e633bb3B63815a386f7012", + BINANCE_ORACLE: "0xB58BFDCE610042311Dc0e034a80Cc7776c1D68f5", BLOCK_NUMBER: 34678080, }, bsc: { comments: "token2 is HAY token and token1 is USDD token", + ADMIN: "0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396", + ACM: "0x4788629ABc6cFCA10F9f969efdEAa1cF70c23555", ACC1: "0x3Ac99C7853b58f4AA38b309D372562a5A88bB9C1", ACC2: "0xA4a04C2D661bB514bB8B478CaCB61145894563ef", ACC3: "0x394d1d517e8269596a7E4Cd1DdaC1C928B3bD8b3", - BUSD_HOLDER: "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3", - USDC_HOLDER: "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3", - USDT_HOLDER: "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3", - USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", - BUSD: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", USDT: "0x55d398326f99059fF775485246999027B3197955", - ADMIN: "0x939bD8d64c0A9583A7Dcea9933f7b21697ab6396", - TREASURY: "0xF322942f644A996A617BD29c16bd7d231d9F35E9", - ACM: "0x4788629ABc6cFCA10F9f969efdEAa1cF70c23555", - RESILIENT_ORACLE: "0x6592b5DE802159F3E74B2486b091D11a8256ab8A", - BINANCE_ORACLE: "0x594810b741d136f1960141C0d8Fb4a91bE78A820", - CHAINLINK_ORACLE: "0x1B2103441A0A108daD8848D8F5d790e4D402921F", - POOL_REGISTRY: "0x9F7b01A536aFA00EF10310A162877fd792cD0666", - SWAP_ROUTER_CORE_POOL: "0x8938E6dA30b59c1E27d5f70a94688A89F7c815a4", - COMPTROLLER_TRON: "0x23b4404E4E5eC5FF5a6FFb70B7d14E3FabF237B0", - COMPTROLLER: "0x94c1495cD4c557f1560Cbd68EAB0d197e6291571", - TRX: "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3", - VUSDT_TRON: "0x281E5378f99A4bc55b295ABc0A3E7eD32Deba059", - VTRX_TRON: "0x836beb2cB723C498136e1119248436A645845F4E", - TRX_HOLDER: "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296", - PANCAKE_SWAP_ROUTER: "0x10ED43C718714eb63d5aA57B78B54704E256024E", - TOKEN2_HOLDER: "0x09702Ea135d9D707DD51f530864f2B9220aAD87B", - TOKEN1_HOLDER: "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296", - TOKEN2: "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", - VTOKEN2: "0xCa2D81AA7C09A1a025De797600A7081146dceEd9", TOKEN1: "0xd17479997F34dd9156Deef8F95A52D81D265be9c", + TOKEN2: "0x0782b6d8c4551B9760e74c0545a9bCD90bdc41E5", VTOKEN1: "0xc3a45ad8812189cAb659aD99E64B1376f6aCD035", - REWARD_DISTRIBUTOR1: "0xBA711976CdF8CF3288bF721f758fB764503Eb1f6", + VTOKEN2: "0xCa2D81AA7C09A1a025De797600A7081146dceEd9", + VUSDT: "0x5e3072305F9caE1c7A82F6Fe9E38811c74922c3B", + COMPTROLLER: "0x94c1495cD4c557f1560Cbd68EAB0d197e6291571", + TOKEN1_HOLDER: "0x3DdfA8eC3052539b6C9549F12cEA2C295cfF5296", + TOKEN2_HOLDER: "0x09702Ea135d9D707DD51f530864f2B9220aAD87B", + USDT_HOLDER: "0x8894E0a0c962CB723c1976a4421c95949bE2D4E3", PSR: "0xfB5bE09a1FA6CFDA075aB1E69FE83ce8324682e4", SHORTFALL: "0xf37530A8a810Fcb501AA0Ecd0B0699388F0F2209", RISKFUND: "0xdF31a28D68A2AB381D42b380649Ead7ae2A76E42", + REWARD_DISTRIBUTOR1: "0xBA711976CdF8CF3288bF721f758fB764503Eb1f6", + TREASURY: "0xF322942f644A996A617BD29c16bd7d231d9F35E9", + POOL_REGISTRY: "0x9F7b01A536aFA00EF10310A162877fd792cD0666", + SWAP_ROUTER_CORE_POOL: "0x8938E6dA30b59c1E27d5f70a94688A89F7c815a4", + RESILIENT_ORACLE: "0x6592b5DE802159F3E74B2486b091D11a8256ab8A", + CHAINLINK_ORACLE: "0x1B2103441A0A108daD8848D8F5d790e4D402921F", + BINANCE_ORACLE: "0x594810b741d136f1960141C0d8Fb4a91bE78A820", BLOCK_NUMBER: 33050804, }, }; diff --git a/tests/hardhat/Fork/liquidation.ts b/tests/hardhat/Fork/liquidation.ts index c60d37a8b..612938fcc 100644 --- a/tests/hardhat/Fork/liquidation.ts +++ b/tests/hardhat/Fork/liquidation.ts @@ -28,42 +28,41 @@ const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; const { - ACM, ACC1, ACC2, ADMIN, + ACM, + PSR, TOKEN1, TOKEN2, VTOKEN1, VTOKEN2, + COMPTROLLER, TOKEN1_HOLDER, TOKEN2_HOLDER, - COMPTROLLER, - BLOCK_NUMBER, RESILIENT_ORACLE, CHAINLINK_ORACLE, + BLOCK_NUMBER, } = getContractAddresses(network as string); const AddressZero = "0x0000000000000000000000000000000000000000"; -let impersonatedTimelock: Signer; -let impersonatedOracleOwner: Signer; -let accessControlManager: AccessControlManager; -let priceOracle: ChainlinkOracle; -let resilientOracle: MockPriceOracle; -let comptroller: Comptroller; -let vTOKEN1: VToken; -let vTOKEN2: VToken; let token1: IERC20; let token2: IERC20; +let vTOKEN1: VToken; +let vTOKEN2: VToken; +let comptroller: Comptroller; let token1Holder: Signer; let token2Holder: Signer; let acc1Signer: Signer; let acc2Signer: Signer; +let impersonatedTimelock: Signer; +let priceOracle: ChainlinkOracle; +let resilientOracle: MockPriceOracle; +let accessControlManager: AccessControlManager; async function configureTimelock() { impersonatedTimelock = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); - impersonatedOracleOwner = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); } async function configureVToken(vTokenAddress: string) { @@ -129,7 +128,7 @@ if (FORKING) { vTOKEN2 = await configureVToken(VTOKEN2); vTOKEN1 = await configureVToken(VTOKEN1); comptroller = Comptroller__factory.connect(COMPTROLLER, impersonatedTimelock); - priceOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedOracleOwner); + priceOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedTimelock); const tupleForToken2 = { asset: TOKEN2, @@ -253,6 +252,9 @@ if (FORKING) { const totalReservesToken1Prev = await vTOKEN1.totalReserves(); const vTOKEN1BalAcc1Prev = await vTOKEN1.balanceOf(ACC1); const vTOKEN1BalAcc2Prev = await vTOKEN1.balanceOf(ACC2); + const psrBalancePrev = await token1.balanceOf(PSR); + + const psrAndreservesSumPrev = psrBalancePrev.add(totalReservesToken1Prev); const borrowBalancePrev = await vTOKEN2.borrowBalanceStored(ACC2); const closeFactor = await comptroller.closeFactorMantissa(); const maxClose = (borrowBalance * closeFactor) / 1e18; @@ -277,7 +279,6 @@ if (FORKING) { const protocolSeizeTokens = Math.floor((seizeTokens * protocolSeizeShareMantissa) / liquidationIncentive); const liquidatorSeizeTokens = Math.floor(seizeTokens - protocolSeizeTokens); - const reserveIncrease = (protocolSeizeTokens * exchangeRateCollateralNew) / 1e18; const borrowBalanceNew = await vTOKEN2.borrowBalanceStored(ACC2); expect(borrowBalancePrev - maxClose).closeTo(borrowBalanceNew, 100); @@ -285,7 +286,11 @@ if (FORKING) { expect(vTOKEN1BalAcc2Prev - vTOKEN1BalAcc2New).to.closeTo(Math.floor(seizeTokens), 100); expect(vTOKEN1BalAcc1New - vTOKEN1BalAcc1Prev).to.closeTo(liquidatorSeizeTokens, 100); - const difference = totalReservesToken1New.sub(totalReservesToken1Prev); + const psrBalanceNew = await token1.balanceOf(PSR); + + const psrAndreservesSumNew = psrBalanceNew.add(totalReservesToken1New); + const difference = psrAndreservesSumNew.sub(psrAndreservesSumPrev); + expect(difference.toString()).to.closeTo(reserveIncrease.toString(), parseUnits("0.00003", 18)); }); }); @@ -347,17 +352,15 @@ if (FORKING) { // repayAmount will be calculated after accruing interest and then using borrowBalanceStored to get the repayAmount. let repayAmount; - if (network == "bsctestnet") repayAmount = 1000000079134225; + if (network == "bsctestnet") repayAmount = 1000000047610436; else if (network == "sepolia") repayAmount = 1000000019025879; - else if (network == "bsc") repayAmount = 1000000056340503; - + else if (network == "bsc") repayAmount = 1000000057174840; const seizeTokens = ratio * repayAmount; const param = { vTokenCollateral: vTOKEN1.address, vTokenBorrowed: vTOKEN2.address, repayAmount: repayAmount, }; - const result = comptroller.connect(acc1Signer).liquidateAccount(ACC2, [param]); await expect(result).to.emit(vTOKEN2, "LiquidateBorrow"); expect(await vTOKEN2.borrowBalanceStored(ACC2)).equals(0); @@ -375,7 +378,7 @@ if (FORKING) { expect(vTOKEN1BalAcc1New - vTOKEN1BalAcc1Prev).to.closeTo(liquidatorSeizeTokens, 1); expect(totalReservesToken1New - totalReservesToken1Prev).to.closeTo( Math.round(reserveIncrease), - parseUnits("0.00003", 18), + parseUnits("0.0003", 18), ); }); }); diff --git a/tests/hardhat/Fork/reduceReservesTest.ts b/tests/hardhat/Fork/reduceReservesTest.ts index c6b5be919..cc88e39c2 100644 --- a/tests/hardhat/Fork/reduceReservesTest.ts +++ b/tests/hardhat/Fork/reduceReservesTest.ts @@ -3,7 +3,7 @@ import { mine } from "@nomicfoundation/hardhat-network-helpers"; import chai from "chai"; import { BigNumber, BigNumberish, Signer } from "ethers"; import { parseUnits } from "ethers/lib/utils"; -import { ethers, upgrades } from "hardhat"; +import { ethers } from "hardhat"; import { convertToUnit } from "../../../helpers/utils"; import { @@ -24,19 +24,19 @@ chai.use(smock.matchers); const FORKING = process.env.FORKING === "true"; const network = process.env.NETWORK_NAME || "bsc"; -const { ACM, ACC1, ACC2, ADMIN, TOKEN1, VTOKEN1, TOKEN1_HOLDER, COMPTROLLER, BLOCK_NUMBER } = getContractAddresses( +const { ACC1, ACC2, ACM, ADMIN, PSR, TOKEN1_HOLDER, TOKEN1, VTOKEN1, COMPTROLLER, BLOCK_NUMBER } = getContractAddresses( network as string, ); -let PROTOCOL_SHARE_RESERVE: string; -let impersonatedTimelock: Signer; let token1Holder: string; let accessControlManager: AccessControlManager; -let comptroller: Comptroller; -let vTOKEN1: VToken; let token1: IERC20; +let vTOKEN1: VToken; + +let comptroller: Comptroller; let acc1Signer: Signer; let acc2Signer: Signer; +let impersonatedTimelock: Signer; let mintAmount: BigNumberish; let TOKEN1BorrowAmount: BigNumberish; @@ -80,16 +80,7 @@ if (FORKING) { vTOKEN1 = await configureVToken(VTOKEN1); comptroller = Comptroller__factory.connect(COMPTROLLER, impersonatedTimelock); - const PSR = await ethers.getContractFactory("ProtocolShareReserve"); - const psr = await upgrades.deployProxy(PSR, [ADMIN, ACC1]); - await psr.deployed(); - - const FakePSR = await smock.fake("ProtocolShareReserve"); - PROTOCOL_SHARE_RESERVE = FakePSR.address; - - await vTOKEN1.connect(impersonatedTimelock).setProtocolShareReserve(PROTOCOL_SHARE_RESERVE); - - FakePSR.updateAssetsState.returns(true); + await vTOKEN1.connect(impersonatedTimelock).setProtocolShareReserve(PSR); await grantPermissions(); @@ -116,11 +107,13 @@ if (FORKING) { let totalCashOld = await vTOKEN1.getCash(); await mintVTokens(acc1Signer, token1, vTOKEN1, mintAmount); let totalCashNew = await vTOKEN1.getCash(); + expect(totalCashNew.sub(totalCashOld)).equals(mintAmount); totalCashOld = totalCashNew; await vTOKEN1.connect(acc2Signer).borrow(TOKEN1BorrowAmount); totalCashNew = await vTOKEN1.getCash(); + expect(totalCashOld.sub(totalCashNew)).equals(TOKEN1BorrowAmount); // MINE 300000 BLOCKS @@ -131,6 +124,7 @@ if (FORKING) { const borrowRatePrior = await vTOKEN1.borrowRatePerBlock(); const totalBorrowsPrior = await vTOKEN1.totalBorrows(); const reserveBefore = await vTOKEN1.totalReserves(); + const psrBalancePrior = await token1.balanceOf(PSR); await vTOKEN1.accrueInterest(); @@ -141,7 +135,11 @@ if (FORKING) { const interestAccumulated = simpleInterestFactor.mul(totalBorrowsPrior).div(convertToUnit(1, 18)); const reserveFactorMantissa = await vTOKEN1.reserveFactorMantissa(); const totalReservesExpected = reserveFactorMantissa.mul(interestAccumulated).div(convertToUnit(1, 18)); - let totalReservesCurrent = BigNumber.from(await vTOKEN1.totalReserves()).sub(reserveBefore); + const psrBalanceNew = await token1.balanceOf(PSR); + + const psrBalanceDiff = psrBalanceNew.sub(psrBalancePrior); + let totalReservesCurrent = BigNumber.from((await vTOKEN1.totalReserves()).add(psrBalanceDiff)).sub(reserveBefore); + expect(totalReservesExpected).equals(totalReservesCurrent); // Calculation of exchange rate @@ -155,6 +153,7 @@ if (FORKING) { .add(badDebt) .sub(await vTOKEN1.totalReserves()); let exchangeRateExpected = cashPlusBorrowsMinusReserves.mul(convertToUnit(1, 18)).div(totalSupply); + expect(exchangeRateExpected).equals(exchangeRateStored); // Reduce reserves @@ -162,16 +161,14 @@ if (FORKING) { totalCashOld = await vTOKEN1.getCash(); const totalReservesOld = await vTOKEN1.totalReserves(); const reduceAmount = totalReservesOld.mul(50).div(100); - const protocolShareBalanceOld = await token1.balanceOf(PROTOCOL_SHARE_RESERVE); + const protocolShareBalanceOld = await token1.balanceOf(PSR); await vTOKEN1.reduceReserves(reduceAmount); - - // totalReservesCurrent = BigNumber.from(await vTOKEN1.totalReserves()).sub(BigNumber.from(reserveBefore)); totalReservesCurrent = await vTOKEN1.totalReserves(); expect(totalReservesOld.sub(totalReservesCurrent)).to.closeTo(reduceAmount, parseUnits("0.0000002", 19)); - const protocolShareBalanceNew = await token1.balanceOf(PROTOCOL_SHARE_RESERVE); + const protocolShareBalanceNew = await token1.balanceOf(PSR); expect(protocolShareBalanceNew.sub(protocolShareBalanceOld)).equals(reduceAmount); totalCashNew = await vTOKEN1.getCash(); diff --git a/tests/hardhat/Fork/supply.ts b/tests/hardhat/Fork/supply.ts index d5ddf0b88..f77c28192 100644 --- a/tests/hardhat/Fork/supply.ts +++ b/tests/hardhat/Fork/supply.ts @@ -34,41 +34,39 @@ const { ACC3, ADMIN, TOKEN1, - VTOKEN1, TOKEN2, + VTOKEN1, VTOKEN2, + COMPTROLLER, TOKEN1_HOLDER, TOKEN2_HOLDER, - COMPTROLLER, - BLOCK_NUMBER, RESILIENT_ORACLE, CHAINLINK_ORACLE, + BLOCK_NUMBER, } = getContractAddresses(network as string); const AddressZero = "0x0000000000000000000000000000000000000000"; -let impersonatedTimelock: Signer; -let impersonatedOracleOwner: Signer; -let accessControlManager: AccessControlManager; -let priceOracle: ChainlinkOracle; -let comptroller: Comptroller; -let vTOKEN1: VToken; -let vTOKEN2: VToken; let token1: IERC20; let token2: IERC20; +let vTOKEN1: VToken; +let vTOKEN2: VToken; +let comptroller: Comptroller; let acc1Signer: Signer; let acc2Signer: Signer; let acc3Signer: Signer; -let token1Holder: string; -let token2Holder: string; +let token1Holder: Signer; +let token2Holder: Signer; +let impersonatedTimelock: Signer; let resilientOracle: MockPriceOracle; +let priceOracle: ChainlinkOracle; +let accessControlManager: AccessControlManager; const blocksToMine: number = 300000; const TOKEN2BorrowAmount = convertToUnit("1", 17); async function configureTimelock() { impersonatedTimelock = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); - impersonatedOracleOwner = await initMainnetUser(ADMIN, ethers.utils.parseUnits("2")); } async function configureVToken(vTokenAddress: string) { @@ -118,7 +116,7 @@ if (FORKING) { vTOKEN1 = await configureVToken(VTOKEN1); comptroller = Comptroller__factory.connect(COMPTROLLER, impersonatedTimelock); - priceOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedOracleOwner); + priceOracle = ChainlinkOracle__factory.connect(CHAINLINK_ORACLE, impersonatedTimelock); const tupleForToken2 = { asset: TOKEN2,