Skip to content

Commit

Permalink
feat: deploy account factory per each credit manager
Browse files Browse the repository at this point in the history
Also add `salt` to `DeployParams`
  • Loading branch information
lekhovitsky committed Jan 15, 2025
1 parent 1df8617 commit 85cc13a
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 39 deletions.
66 changes: 46 additions & 20 deletions contracts/factories/CreditFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {IAccountFactory} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IAccountFactory.sol";
import {ICreditConfiguratorV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditConfiguratorV3.sol";
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
import {IPoolV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolV3.sol";
Expand All @@ -15,20 +16,21 @@ import {Call, DeployParams, DeployResult} from "../interfaces/Types.sol";

import {CallBuilder} from "../libraries/CallBuilder.sol";
import {
DOMAIN_ACCOUNT_FACTORY,
DOMAIN_ADAPTER,
DOMAIN_CREDIT_MANAGER,
DOMAIN_DEGEN_NFT,
AP_CREDIT_CONFIGURATOR,
AP_CREDIT_FACADE,
AP_CREDIT_FACTORY,
AP_INSTANCE_MANAGER_PROXY,
AP_WETH_TOKEN,
NO_VERSION_CONTROL
} from "../libraries/ContractLiterals.sol";

import {AbstractFactory} from "./AbstractFactory.sol";

struct CreditManagerParams {
address accountFactory;
uint8 maxEnabledTokens;
uint16 feeInterest;
uint16 feeLiquidation;
Expand All @@ -38,6 +40,7 @@ struct CreditManagerParams {
uint128 minDebt;
uint128 maxDebt;
string name;
DeployParams accountFactoryParams;
}

struct CreditFacadeParams {
Expand Down Expand Up @@ -113,10 +116,12 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
(CreditManagerParams memory params, CreditFacadeParams memory facadeParams) =
abi.decode(encodedParams, (CreditManagerParams, CreditFacadeParams));

address creditManager = _deployCreditManager(msg.sender, pool, params);
address accountFactory = _deployAccountFactory(msg.sender, params.accountFactoryParams);
address creditManager = _deployCreditManager(msg.sender, pool, accountFactory, params);
address creditConfigurator = _deployCreditConfigurator(msg.sender, creditManager);
address creditFacade = _deployCreditFacade(msg.sender, creditManager, facadeParams);

IAccountFactory(accountFactory).addCreditManager(creditManager);
ICreditManagerV3(creditManager).setCreditConfigurator(creditConfigurator);

return DeployResult({
Expand Down Expand Up @@ -253,12 +258,43 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
// INTERNALS //
// --------- //

function _deployCreditManager(address marketConfigurator, address pool, CreditManagerParams memory params)
function _deployAccountFactory(address marketConfigurator, DeployParams memory params) internal returns (address) {
address decodedOwner = abi.decode(params.constructorParams, (address));
if (decodedOwner != _getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL)) {
revert InvalidConstructorParamsException();
}

return _deployLatestPatch({
contractType: _getContractType(DOMAIN_ACCOUNT_FACTORY, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: keccak256(abi.encode(params.salt, marketConfigurator))
});
}

function _computeAccountFactoryAddress(address marketConfigurator, DeployParams memory params)
internal
view
returns (address)
{
return _computeAddressLatestPatch({
contractType: _getContractType(DOMAIN_ACCOUNT_FACTORY, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: keccak256(abi.encode(params.salt, marketConfigurator)),
deployer: address(this)
});
}

function _deployCreditManager(
address marketConfigurator,
address pool,
address accountFactory,
CreditManagerParams memory params
) internal returns (address) {
bytes32 postfix = _getTokenSpecificPostfix(IPoolV3(pool).asset());
bytes memory constructorParams = _buildCreditManagerConstructorParams(marketConfigurator, pool, params);
bytes memory constructorParams =
_buildCreditManagerConstructorParams(marketConfigurator, pool, accountFactory, params);
return _deployLatestPatch({
contractType: _getContractType(DOMAIN_CREDIT_MANAGER, postfix),
minorVersion: version,
Expand All @@ -272,8 +308,10 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
view
returns (address)
{
address accountFactory = _computeAccountFactoryAddress(marketConfigurator, params.accountFactoryParams);
bytes32 postfix = _getTokenSpecificPostfix(IPoolV3(pool).asset());
bytes memory constructorParams = _buildCreditManagerConstructorParams(marketConfigurator, pool, params);
bytes memory constructorParams =
_buildCreditManagerConstructorParams(marketConfigurator, pool, accountFactory, params);
return _computeAddressLatestPatch({
contractType: _getContractType(DOMAIN_CREDIT_MANAGER, postfix),
minorVersion: version,
Expand All @@ -286,15 +324,15 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
function _buildCreditManagerConstructorParams(
address marketConfigurator,
address pool,
address accountFactory,
CreditManagerParams memory params
) internal view returns (bytes memory) {
address contractsRegister = IMarketConfigurator(marketConfigurator).contractsRegister();
address priceOracle = IContractsRegister(contractsRegister).getPriceOracle(pool);

// TODO: ensure that account factory is registered, add manager to it
return abi.encode(
pool,
params.accountFactory,
accountFactory,
priceOracle,
params.maxEnabledTokens,
params.feeInterest,
Expand Down Expand Up @@ -350,25 +388,13 @@ contract CreditFactory is AbstractFactory, ICreditFactory {
returns (address)
{
address decodedCreditManager = abi.decode(params.constructorParams, (address));

if (decodedCreditManager != creditManager) revert InvalidConstructorParamsException();

// NOTE: allowing a previously forbidden adapter is considered a valid operation,
// so we just return the contract address if it's already deployed
address adapter = _computeAddressLatestPatch({
contractType: _getContractType(DOMAIN_ADAPTER, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: bytes32(bytes20(marketConfigurator)),
deployer: address(this)
});
if (adapter.code.length != 0) return adapter;

return _deployLatestPatch({
contractType: _getContractType(DOMAIN_ADAPTER, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: bytes32(bytes20(marketConfigurator))
salt: keccak256(abi.encode(params.salt, marketConfigurator))
});
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/factories/InterestRateModelFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract InterestRateModelFactory is AbstractMarketFactory, IInterestRateModelFa
contractType: _getContractType(DOMAIN_IRM, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: bytes32(bytes20(pool))
salt: keccak256(abi.encode(params.salt, msg.sender))
});

return DeployResult({
Expand Down
2 changes: 1 addition & 1 deletion contracts/factories/LossPolicyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ contract LossPolicyFactory is AbstractMarketFactory, ILossPolicyFactory {
contractType: _getContractType(DOMAIN_LOSS_POLICY, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: bytes32(bytes20(msg.sender))
salt: keccak256(abi.encode(params.salt, msg.sender))
});

return DeployResult({
Expand Down
2 changes: 1 addition & 1 deletion contracts/factories/RateKeeperFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ contract RateKeeperFactory is AbstractMarketFactory, IRateKeeperFactory {
contractType: _getContractType(DOMAIN_RATE_KEEPER, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: bytes32(bytes20(msg.sender))
salt: keccak256(abi.encode(params.salt, msg.sender))
});

return DeployResult({
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct SignedProposal {

struct DeployParams {
bytes32 postfix;
bytes32 salt;
bytes constructorParams;
}

Expand Down
3 changes: 2 additions & 1 deletion contracts/libraries/ContractLiterals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR";
bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER";
bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE";
bytes32 constant AP_LOSS_POLICY_DEFAULT = "LOSS_POLICY::DEFAULT";
bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT";

bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE";
bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR";

bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE";
bytes32 constant AP_ACCOUNT_FACTORY = "ACCOUNT_FACTORY";

bytes32 constant AP_TREASURY = "TREASURY";
bytes32 constant AP_GEAR_TOKEN = "GEAR_TOKEN";
Expand Down Expand Up @@ -61,6 +61,7 @@ bytes32 constant AP_RATE_KEEPER_FACTORY = "RATE_KEEPER_FACTORY";
bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY";
bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY";

bytes32 constant DOMAIN_ACCOUNT_FACTORY = "ACCOUNT_FACTORY";
bytes32 constant DOMAIN_POOL = "POOL";
bytes32 constant DOMAIN_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant DOMAIN_ADAPTER = "ADAPTER";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.22;
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {Test} from "forge-std/Test.sol";

Expand All @@ -14,7 +14,7 @@ contract TargetMock {
fallback() external payable {}
}

contract GovernorTest is Test {
contract GovernorUnitTest is Test {
Governor governor;

address timeLock;
Expand Down
10 changes: 10 additions & 0 deletions contracts/test/helpers/GlobalSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {IInstanceManager} from "../../interfaces/IInstanceManager.sol";
import {IWETH} from "@gearbox-protocol/core-v3/contracts/interfaces/external/IWETH.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {
AP_ACCOUNT_FACTORY_DEFAULT,
AP_PRICE_FEED_STORE,
AP_INTEREST_RATE_MODEL_FACTORY,
AP_CREDIT_FACTORY,
Expand Down Expand Up @@ -55,6 +56,7 @@ import {Governor} from "../../market/Governor.sol";
// Core contracts
import {PoolV3} from "@gearbox-protocol/core-v3/contracts/pool/PoolV3.sol";
import {PoolQuotaKeeperV3} from "@gearbox-protocol/core-v3/contracts/pool/PoolQuotaKeeperV3.sol";
import {DefaultAccountFactoryV3} from "@gearbox-protocol/core-v3/contracts/core/DefaultAccountFactoryV3.sol";
import {PriceOracleV3} from "@gearbox-protocol/core-v3/contracts/core/PriceOracleV3.sol";
import {LinearInterestRateModelV3} from "@gearbox-protocol/core-v3/contracts/pool/LinearInterestRateModelV3.sol";
import {TumblerV3} from "@gearbox-protocol/core-v3/contracts/pool/TumblerV3.sol";
Expand Down Expand Up @@ -262,6 +264,14 @@ contract GlobalSetup is Test, InstanceManagerHelper {
UploadableContract({initCode: type(GaugeV3).creationCode, contractType: AP_RATE_KEEPER_GAUGE, version: 3_10})
);

contractsToUpload.push(
UploadableContract({
initCode: type(DefaultAccountFactoryV3).creationCode,
contractType: AP_ACCOUNT_FACTORY_DEFAULT,
version: 3_10
})
);

contractsToUpload.push(
UploadableContract({
initCode: type(PriceOracleV3).creationCode,
Expand Down
30 changes: 19 additions & 11 deletions contracts/test/suite/NewChainDeploySuite.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {IWETH} from "@gearbox-protocol/core-v3/contracts/interfaces/external/IWE
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {
AP_PRICE_FEED_STORE,
AP_INSTANCE_MANAGER_PROXY,
AP_INTEREST_RATE_MODEL_FACTORY,
AP_CREDIT_FACTORY,
AP_POOL_FACTORY,
Expand Down Expand Up @@ -110,6 +111,7 @@ contract NewChainDeploySuite is Test, GlobalSetup {
address ap = instanceManager.addressProvider();

address mcf = IAddressProvider(ap).getAddressOrRevert(AP_MARKET_CONFIGURATOR_FACTORY, NO_VERSION_CONTROL);
address imProxy = IAddressProvider(ap).getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL);

address poolFactory = IAddressProvider(ap).getAddressOrRevert(AP_POOL_FACTORY, 3_10);

Expand All @@ -126,29 +128,34 @@ contract NewChainDeploySuite is Test, GlobalSetup {

address pool = MarketConfigurator(mc).previewCreateMarket(3_10, WETH, name, symbol);

bytes memory interestRateModelParams =
abi.encode(uint16(100), uint16(200), uint16(100), uint16(100), uint16(200), uint16(300), false);
bytes memory rateKeeperParams = abi.encode(pool, 7 days);
bytes memory lossPolicyParams = abi.encode(pool, ap);
DeployParams memory interestRateModelParams = DeployParams({
postfix: "LINEAR",
salt: 0,
constructorParams: abi.encode(100, 200, 100, 100, 200, 300, false)
});
DeployParams memory rateKeeperParams =
DeployParams({postfix: "TUMBLER", salt: 0, constructorParams: abi.encode(pool, 7 days)});
DeployParams memory lossPolicyParams =
DeployParams({postfix: "DEFAULT", salt: 0, constructorParams: abi.encode(pool, ap)});

address poolFromMarket = MarketConfigurator(mc).createMarket({
minorVersion: 3_10,
underlying: WETH,
name: name,
symbol: symbol,
interestRateModelParams: DeployParams("LINEAR", interestRateModelParams),
rateKeeperParams: DeployParams("TUMBLER", rateKeeperParams),
lossPolicyParams: DeployParams("DEFAULT", lossPolicyParams),
interestRateModelParams: interestRateModelParams,
rateKeeperParams: rateKeeperParams,
lossPolicyParams: lossPolicyParams,
underlyingPriceFeed: CHAINLINK_ETH_USD
});

assertEq(pool, poolFromMarket);

address mainnetAF = 0x444CD42BaEdDEB707eeD823f7177b9ABcC779C04;
address botList = 0x6B24183313074ABb6E3B30Ea206F20c12205053a;

DeployParams memory accountFactoryParams =
DeployParams({postfix: "DEFAULT", salt: 0, constructorParams: abi.encode(imProxy)});
CreditManagerParams memory creditManagerParams = CreditManagerParams({
accountFactory: mainnetAF,
maxEnabledTokens: 4,
feeInterest: 10_00,
feeLiquidation: 1_50,
Expand All @@ -157,7 +164,8 @@ contract NewChainDeploySuite is Test, GlobalSetup {
liquidationPremiumExpired: 1_50,
minDebt: 1e18,
maxDebt: 20e18,
name: "Credit Manager ETH"
name: "Credit Manager ETH",
accountFactoryParams: accountFactoryParams
});

CreditFacadeParams memory facadeParams =
Expand All @@ -172,7 +180,7 @@ contract NewChainDeploySuite is Test, GlobalSetup {
MarketConfigurator(mc).configureCreditSuite(
cm,
abi.encodeCall(
IConfigureActions.allowAdapter, (DeployParams("BALANCER_VAULT", abi.encode(cm, balancerVault)))
IConfigureActions.allowAdapter, (DeployParams("BALANCER_VAULT", 0, abi.encode(cm, balancerVault)))
)
);

Expand Down

0 comments on commit 85cc13a

Please sign in to comment.