diff --git a/contracts/test/suite/AttachTestBase.sol b/contracts/test/suite/AttachTestBase.sol index 548fb8b..a533eac 100644 --- a/contracts/test/suite/AttachTestBase.sol +++ b/contracts/test/suite/AttachTestBase.sol @@ -3,23 +3,34 @@ // (c) Gearbox Foundation, 2025. pragma solidity ^0.8.23; -import {Test} from "forge-std/Test.sol"; +import {Test, stdStorage, StdStorage} from "forge-std/Test.sol"; import {VmSafe} from "forge-std/Vm.sol"; import {console} from "forge-std/console.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +import {LibString} from "@solady/utils/LibString.sol"; + +import {ITumblerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ITumblerV3.sol"; import {IMarketConfigurator} from "../../interfaces/IMarketConfigurator.sol"; import {AuditReport, Bytecode, DeployParams} from "../../interfaces/Types.sol"; -import {CreditFacadeParams, CreditManagerParams} from "../../interfaces/factories/ICreditConfigureActions.sol"; +import { + ICreditConfigureActions, + CreditFacadeParams, + CreditManagerParams +} from "../../interfaces/factories/ICreditConfigureActions.sol"; import {IPoolConfigureActions} from "../../interfaces/factories/IPoolConfigureActions.sol"; +import {IPriceOracleConfigureActions} from "../../interfaces/factories/IPriceOracleConfigureActions.sol"; import {Domain} from "../../libraries/Domain.sol"; import {AttachBase} from "./AttachBase.sol"; contract AttachTestBase is AttachBase, Test { + using LibString for bytes32; + using stdStorage for StdStorage; + // ---- // // CORE // // ---- // @@ -40,6 +51,8 @@ contract AttachTestBase is AttachBase, Test { } function _addPublicDomain(bytes32 domain) internal { + if (bytecodeRepository.isPublicDomain(domain)) return; + vm.prank(crossChainGovernance); instanceManager.configureGlobal( address(bytecodeRepository), abi.encodeCall(bytecodeRepository.addPublicDomain, (domain)) @@ -47,6 +60,16 @@ contract AttachTestBase is AttachBase, Test { } function _uploadContract(bytes32 contractType, uint256 version, bytes memory initCode) internal { + bytes32 allowedBytecodeHash = bytecodeRepository.getAllowedBytecodeHash(contractType, version); + if (allowedBytecodeHash != 0) { + bytes memory uploadedInitCode = bytecodeRepository.getBytecode(allowedBytecodeHash).initCode; + require( + keccak256(initCode) == keccak256(uploadedInitCode), + string.concat("Bytecode mismatch for ", contractType.fromSmallString(), " v", vm.toString(version)) + ); + return; + } + Bytecode memory bytecode; bytecode.contractType = contractType; bytecode.version = version; @@ -65,6 +88,10 @@ contract AttachTestBase is AttachBase, Test { bytecodeRepository.submitAuditReport(bytecodeHash, auditReport); if (bytecodeRepository.isPublicDomain(Domain.extractDomain(contractType))) { + if (bytecodeRepository.getContractTypeOwner(contractType) != address(0)) { + stdstore.target(address(bytecodeRepository)).sig("getContractTypeOwner(bytes32)").with_key(contractType) + .checked_write(author.addr); + } bytecodeRepository.allowPublicContract(bytecodeHash); } else { vm.prank(crossChainGovernance); @@ -74,6 +101,30 @@ contract AttachTestBase is AttachBase, Test { } } + function _deploy(bytes32 contractType, uint256 version, bytes memory constructorParams) internal returns (address) { + return bytecodeRepository.deploy({ + contractType: contractType, version: version, constructorParams: constructorParams, salt: "GEARBOX" + }); + } + + function _addPriceFeed(address priceFeed, uint32 stalenessPeriod, string memory name) internal { + if (priceFeedStore.isKnownPriceFeed(priceFeed)) return; + + vm.prank(instanceOwner); + instanceManager.configureLocal( + address(priceFeedStore), abi.encodeCall(priceFeedStore.addPriceFeed, (priceFeed, stalenessPeriod, name)) + ); + } + + function _allowPriceFeed(address token, address priceFeed) internal { + if (priceFeedStore.isAllowedPriceFeed(token, priceFeed)) return; + + vm.prank(instanceOwner); + instanceManager.configureLocal( + address(priceFeedStore), abi.encodeCall(priceFeedStore.allowPriceFeed, (token, priceFeed)) + ); + } + // ------- // // MARKETS // // ------- // @@ -103,10 +154,7 @@ contract AttachTestBase is AttachBase, Test { address curator = makeAddr("MockCurator"); vm.prank(curator); configurator = marketConfiguratorFactory.createMarketConfigurator({ - emergencyAdmin: address(0), - adminFeeTreasury: address(0), - curatorName: "MockCurator", - deployGovernor: false + emergencyAdmin: address(0), adminFeeTreasury: address(0), curatorName: "MockCurator", deployGovernor: false }); } @@ -117,10 +165,7 @@ contract AttachTestBase is AttachBase, Test { deal({token: underlying, to: address(marketConfigurator), give: 1e5}); pool = marketConfigurator.previewCreateMarket({ - minorVersion: 3_10, - underlying: underlying, - name: name, - symbol: symbol + minorVersion: 3_10, underlying: underlying, name: name, symbol: symbol }); vm.prank(riskCurator); @@ -130,15 +175,13 @@ contract AttachTestBase is AttachBase, Test { name: name, symbol: symbol, interestRateModelParams: DeployParams({ - postfix: "LINEAR", - salt: "SALT", - constructorParams: abi.encode(5000, 9000, 0, 100, 200, 700, false) + postfix: "LINEAR", salt: "GEARBOX", constructorParams: abi.encode(5000, 9000, 0, 100, 200, 700, false) + }), + rateKeeperParams: DeployParams({ + postfix: "TUMBLER", salt: "GEARBOX", constructorParams: abi.encode(pool, 1 days) }), - rateKeeperParams: DeployParams({postfix: "TUMBLER", salt: "SALT", constructorParams: abi.encode(pool, 1 days)}), lossPolicyParams: DeployParams({ - postfix: "ALIASED", - salt: "SALT", - constructorParams: abi.encode(pool, ADDRESS_PROVIDER) + postfix: "ALIASED", salt: "GEARBOX", constructorParams: abi.encode(pool, ADDRESS_PROVIDER) }), underlyingPriceFeed: priceFeed }); @@ -157,7 +200,7 @@ contract AttachTestBase is AttachBase, Test { pool: pool, encdodedParams: abi.encode( CreditManagerParams({ - maxEnabledTokens: 1, + maxEnabledTokens: 2, feeInterest: 50_00, feeLiquidation: 100, liquidationPremium: 100, @@ -167,18 +210,78 @@ contract AttachTestBase is AttachBase, Test { maxDebt: maxDebt, name: name, accountFactoryParams: DeployParams({ - postfix: "DEFAULT", - salt: "SALT", - constructorParams: abi.encode(ADDRESS_PROVIDER) + postfix: "DEFAULT", salt: "GEARBOX", constructorParams: abi.encode(ADDRESS_PROVIDER) }) }), CreditFacadeParams({degenNFT: address(0), expirable: false, migrateBotList: false}) ) }); - marketConfigurator.configurePool( - pool, abi.encodeCall(IPoolConfigureActions.setCreditManagerDebtLimit, (creditManager, debtLimit)) - ); + if (debtLimit != 0) { + marketConfigurator.configurePool( + pool, abi.encodeCall(IPoolConfigureActions.setCreditManagerDebtLimit, (creditManager, debtLimit)) + ); + } vm.stopPrank(); } + + struct TokenConfig { + address token; + address priceFeed; + address reservePriceFeed; + uint96 quotaLimit; + uint16 quotaRate; + } + + function _addToken(address pool, TokenConfig memory config) internal { + vm.startPrank(riskCurator); + + marketConfigurator.addToken({pool: pool, token: config.token, priceFeed: config.priceFeed}); + + if (config.reservePriceFeed != address(0)) { + marketConfigurator.configurePriceOracle( + pool, + abi.encodeCall( + IPriceOracleConfigureActions.setReservePriceFeed, (config.token, config.reservePriceFeed) + ) + ); + } + if (config.quotaLimit != 0) { + marketConfigurator.configurePool( + pool, abi.encodeCall(IPoolConfigureActions.setTokenLimit, (config.token, config.quotaLimit)) + ); + } + if (config.quotaRate != 0) { + marketConfigurator.configureRateKeeper( + pool, abi.encodeCall(ITumblerV3.setRate, (config.token, config.quotaRate)) + ); + } + + vm.stopPrank(); + } + + function _addCollateralToken(address creditManager, address token, uint16 lt) internal { + vm.prank(riskCurator); + marketConfigurator.configureCreditSuite( + creditManager, abi.encodeCall(ICreditConfigureActions.addCollateralToken, (token, lt)) + ); + } + + function _allowAdapter(address creditManager, bytes32 postfix, bytes memory constructorParams) internal { + vm.prank(riskCurator); + marketConfigurator.configureCreditSuite( + creditManager, + abi.encodeCall( + ICreditConfigureActions.allowAdapter, + (DeployParams({postfix: postfix, salt: "GEARBOX", constructorParams: constructorParams})) + ) + ); + } + + function _configureAdapter(address creditManager, address targetContract, bytes memory data) internal { + vm.prank(riskCurator); + marketConfigurator.configureCreditSuite( + creditManager, abi.encodeCall(ICreditConfigureActions.configureAdapterFor, (targetContract, data)) + ); + } } diff --git a/foundry.lock b/foundry.lock index 1fa3f4b..85f1b3c 100644 --- a/foundry.lock +++ b/foundry.lock @@ -25,8 +25,8 @@ }, "lib/forge-std": { "tag": { - "name": "v1.11.0", - "rev": "8e40513d678f392f398620b3ef2b418648b33e89" + "name": "v1.14.0", + "rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6" } } } \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 1c62d56..6a5d5f1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,4 +15,7 @@ gas_limit = 9223372036854775807 # the gas limit in tests [fuzz] max_test_rejects = 1000000 -runs = 256 \ No newline at end of file +runs = 256 + +[lint] +lint_on_build = false \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std index 8e40513..1801b05 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 8e40513d678f392f398620b3ef2b418648b33e89 +Subproject commit 1801b0541f4fda118a10798fd3486bb7051c5dd6