Skip to content

Commit

Permalink
feat: add morpho strategy and factory (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xsambugs authored Jan 7, 2025
1 parent 7a13d4b commit c239729
Show file tree
Hide file tree
Showing 3 changed files with 589 additions and 0 deletions.
127 changes: 127 additions & 0 deletions src/strategies/instances/morpho/MorphoStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.22;

import { IERC4626, IERC20 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IEarnStrategy, StrategyId, IEarnVault } from "@balmy/earn-core/interfaces/IEarnStrategy.sol";

Check warning on line 5 in src/strategies/instances/morpho/MorphoStrategy.sol

View workflow job for this annotation

GitHub Actions / lint

imported name IEarnStrategy is not used
import { Clone } from "../base/Clone.sol";
import { IGlobalEarnRegistry } from "../../../interfaces/IGlobalEarnRegistry.sol";
import { MorphoConnector } from "../../layers/connector/morpho/MorphoConnector.sol";
import { ExternalFees } from "../../layers/fees/external/ExternalFees.sol";
import { ExternalGuardian } from "../../layers/guardian/external/ExternalGuardian.sol";
import { RegistryBasedCreationValidation } from
"../../layers/creation-validation/external/RegistryBasedCreationValidation.sol";
import { ExternalLiquidityMining } from "../../layers/liquidity-mining/external/ExternalLiquidityMining.sol";
import { BaseStrategy } from "../base/BaseStrategy.sol";

contract MorphoStrategy is
BaseStrategy,
Clone,
MorphoConnector,
ExternalLiquidityMining,
ExternalFees,
ExternalGuardian,
RegistryBasedCreationValidation
{
// slither-disable-next-line reentrancy-benign
function initAndRegister(
address owner,
bytes calldata creationValidationData,
bytes calldata guardianData,
bytes calldata feesData,
bytes calldata liquidityMiningData
)
external
returns (StrategyId strategyId_)
{
strategyId_ = _baseStrategy_registerStrategy(owner);
init({
creationValidationData: creationValidationData,
guardianData: guardianData,
feesData: feesData,
liquidityMiningData: liquidityMiningData
});
}

// slither-disable-next-line reentrancy-benign
function initWithId(
StrategyId strategyId_,
bytes calldata creationValidationData,
bytes calldata guardianData,
bytes calldata feesData,
bytes calldata liquidityMiningData
)
external
{
_strategyId = strategyId_;
init({
creationValidationData: creationValidationData,
guardianData: guardianData,
feesData: feesData,
liquidityMiningData: liquidityMiningData
});
}

// slither-disable-next-line reentrancy-benign
function init(
bytes calldata creationValidationData,
bytes calldata guardianData,
bytes calldata feesData,
bytes calldata liquidityMiningData
)
public
initializer
{
_connector_init();
_creationValidation_init(creationValidationData);
_guardian_init(guardianData);
_fees_init(feesData);
_liquidity_mining_init(liquidityMiningData);
}

function strategyId()
public
view
override(ExternalLiquidityMining, ExternalFees, ExternalGuardian, RegistryBasedCreationValidation, BaseStrategy)
returns (StrategyId)
{
return BaseStrategy.strategyId();
}

// Immutable params:
// 1. Earn Vault (20B)
// 2. Global Registry (20B)
// 3. Morpho vault (20B)
// 4. Asset (20B)

function _earnVault() internal view override returns (IEarnVault) {
return IEarnVault(_getArgAddress(0));
}

function globalRegistry()
public
view
override(ExternalLiquidityMining, ExternalFees, ExternalGuardian, RegistryBasedCreationValidation, MorphoConnector)
returns (IGlobalEarnRegistry)
{
return IGlobalEarnRegistry(_getArgAddress(20));
}

// slither-disable-next-line naming-convention
function ERC4626Vault() public view override returns (IERC4626) {
return IERC4626(_getArgAddress(40));
}

function _asset() internal view override returns (IERC20) {
return IERC20(_getArgAddress(60));
}

// slither-disable-next-line naming-convention,dead-code
function _fees_underlying_asset() internal view override returns (address asset) {
return _connector_asset();
}

// slither-disable-next-line naming-convention,dead-code
function _guardian_rescueFee() internal view override returns (uint16) {
return _getFees().rescueFee;
}
}
170 changes: 170 additions & 0 deletions src/strategies/instances/morpho/MorphoStrategyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.22;

import { IEarnVault } from "@balmy/earn-core/interfaces/IEarnStrategy.sol";
import { StrategyId, StrategyIdConstants } from "@balmy/earn-core/types/StrategyId.sol";
import { IEarnBalmyStrategy } from "../../../interfaces/IEarnBalmyStrategy.sol";
import { IGlobalEarnRegistry } from "../../../interfaces/IGlobalEarnRegistry.sol";
import { BaseStrategyFactory } from "../base/BaseStrategyFactory.sol";
import { MorphoStrategy, IGlobalEarnRegistry, IERC4626 } from "./MorphoStrategy.sol";

struct MorphoStrategyData {
IEarnVault earnVault;
IGlobalEarnRegistry globalRegistry;
IERC4626 morphoVault;
bytes creationValidationData;
bytes guardianData;
bytes feesData;
bytes liquidityMiningData;
}

contract MorphoStrategyFactory is BaseStrategyFactory {
constructor(MorphoStrategy implementation_) BaseStrategyFactory(implementation_) { }

function cloneStrategy(MorphoStrategyData calldata strategyData) public returns (MorphoStrategy clone) {
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone(immutableData);
clone = MorphoStrategy(payable(address(clone_)));
emit StrategyCloned(clone, StrategyIdConstants.NO_STRATEGY);
clone.init({
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
}

function cloneStrategyAndRegister(
address owner,
MorphoStrategyData calldata strategyData
)
external
returns (MorphoStrategy clone, StrategyId strategyId)
{
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone(immutableData);
clone = MorphoStrategy(payable(address(clone_)));
strategyId = clone.initAndRegister({
owner: owner,
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
// slither-disable-next-line reentrancy-events
emit StrategyCloned(clone, strategyId);
}

function cloneStrategyWithId(
StrategyId strategyId,
MorphoStrategyData calldata strategyData
)
external
returns (MorphoStrategy clone)
{
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone(immutableData);
clone = MorphoStrategy(payable(address(clone_)));
emit StrategyCloned(clone, strategyId);
clone.initWithId({
strategyId_: strategyId,
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
}

function clone2Strategy(
MorphoStrategyData calldata strategyData,
bytes32 salt
)
external
returns (MorphoStrategy clone)
{
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone2(immutableData, salt);
clone = MorphoStrategy(payable(address(clone_)));
emit StrategyCloned(clone, StrategyIdConstants.NO_STRATEGY);
clone.init({
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
}

function clone2StrategyAndRegister(
address owner,
MorphoStrategyData calldata strategyData,
bytes32 salt
)
external
returns (MorphoStrategy clone, StrategyId strategyId)
{
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone2(immutableData, salt);
clone = MorphoStrategy(payable(address(clone_)));
strategyId = clone.initAndRegister({
owner: owner,
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
// slither-disable-next-line reentrancy-events
emit StrategyCloned(clone, strategyId);
}

function clone2StrategyWithId(
StrategyId strategyId,
MorphoStrategyData calldata strategyData,
bytes32 salt
)
external
returns (MorphoStrategy clone)
{
bytes memory immutableData = _calculateImmutableData(strategyData);
IEarnBalmyStrategy clone_ = _clone2(immutableData, salt);
clone = MorphoStrategy(payable(address(clone_)));
emit StrategyCloned(clone, strategyId);
clone.initWithId({
strategyId_: strategyId,
creationValidationData: strategyData.creationValidationData,
guardianData: strategyData.guardianData,
feesData: strategyData.feesData,
liquidityMiningData: strategyData.liquidityMiningData
});
}

function addressOfClone2(
IEarnVault earnVault,
IGlobalEarnRegistry globalRegistry,
IERC4626 erc4626Vault,
bytes32 salt
)
external
view
returns (address clone)
{
bytes memory immutableData = _calculateImmutableData(earnVault, globalRegistry, erc4626Vault);
return _addressOfClone2(immutableData, salt);
}

function _calculateImmutableData(MorphoStrategyData calldata strategyData) internal view returns (bytes memory) {
return _calculateImmutableData(strategyData.earnVault, strategyData.globalRegistry, strategyData.morphoVault);
}

function _calculateImmutableData(
IEarnVault earnVault,
IGlobalEarnRegistry globalRegistry,
IERC4626 erc4626Vault
)
internal
view
returns (bytes memory)
{
address asset = erc4626Vault.asset();
return abi.encodePacked(earnVault, globalRegistry, erc4626Vault, asset);
}
}
Loading

0 comments on commit c239729

Please sign in to comment.