From 2de6f86a09c08beed222b0318a905a2a14562964 Mon Sep 17 00:00:00 2001 From: Dima Lekhovitsky Date: Sun, 11 Jan 2026 19:30:57 +0200 Subject: [PATCH 1/2] feat: updates in attach test base --- contracts/test/suite/AttachTestBase.sol | 146 ++++++++++++++++++++---- foundry.lock | 4 +- lib/forge-std | 2 +- 3 files changed, 125 insertions(+), 27 deletions(-) diff --git a/contracts/test/suite/AttachTestBase.sol b/contracts/test/suite/AttachTestBase.sol index 548fb8b..6c53c07 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,11 @@ contract AttachTestBase is AttachBase, Test { } function _uploadContract(bytes32 contractType, uint256 version, bytes memory initCode) internal { + if (bytecodeRepository.getAllowedBytecodeHash(contractType, version) != 0) { + console.log(contractType.fromSmallString(), "version", version, "is already uploaded"); + return; + } + Bytecode memory bytecode; bytecode.contractType = contractType; bytecode.version = version; @@ -65,6 +83,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 +96,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 +149,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 +160,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 +170,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 +195,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 +205,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/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 From dae649ab8e10d11d24d4575a90365de1d0408a3b Mon Sep 17 00:00:00 2001 From: Dima Lekhovitsky Date: Sun, 11 Jan 2026 20:44:58 +0200 Subject: [PATCH 2/2] fix: revert on bytecode mismatch --- contracts/test/suite/AttachTestBase.sol | 9 +++++++-- foundry.toml | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/contracts/test/suite/AttachTestBase.sol b/contracts/test/suite/AttachTestBase.sol index 6c53c07..a533eac 100644 --- a/contracts/test/suite/AttachTestBase.sol +++ b/contracts/test/suite/AttachTestBase.sol @@ -60,8 +60,13 @@ contract AttachTestBase is AttachBase, Test { } function _uploadContract(bytes32 contractType, uint256 version, bytes memory initCode) internal { - if (bytecodeRepository.getAllowedBytecodeHash(contractType, version) != 0) { - console.log(contractType.fromSmallString(), "version", version, "is already uploaded"); + 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; } 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