Skip to content

Commit 023f80b

Browse files
feat: add MorphoVaultV1PositionManager library
Co-authored-by: HareemSaad <hareems20014@outlook.com>
1 parent f018897 commit 023f80b

4 files changed

Lines changed: 1132 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.28;
3+
4+
import {Library} from "./Library.sol";
5+
import {BaseAccount} from "../accounts/BaseAccount.sol";
6+
import {IERC20} from "forge-std/src/interfaces/IERC20.sol";
7+
import {IERC4626} from "forge-std/src/interfaces/IERC4626.sol";
8+
import {IMetaMorphoV1_1} from "./interfaces/morpho/IMetaMorphoV1_1.sol";
9+
10+
/**
11+
* @title MorphoVaultV1PositionManager
12+
* @dev Contract for managing Morpho Vault V1.1 through deposit and withdraw operations.
13+
* It leverages BaseAccount contract to interact with the Morpho Vault V1.1 protocol, enabling automated position management.
14+
*/
15+
contract MorphoVaultV1PositionManager is Library {
16+
/**
17+
* @title MorphoVaultV1PositionManagerConfig
18+
* @notice Configuration struct for Morpho Vault V1.1 Position Manager
19+
* @dev Used to define parameters for interacting with Morpho Vault V1.1 protocol
20+
* @param inputAccount The Base Account from which transactions will be initiated
21+
* @param outputAccount The Base Account that will receive withdrawals
22+
* @param vaultAddress Address of the Morpho Vault V1
23+
* @param assetAddress Address of the underlying asset to manage
24+
*/
25+
struct MorphoVaultV1PositionManagerConfig {
26+
BaseAccount inputAccount;
27+
BaseAccount outputAccount;
28+
address vaultAddress;
29+
address assetAddress;
30+
}
31+
32+
/// @notice Holds the current configuration for the MorphoVaultV1PositionManager.
33+
MorphoVaultV1PositionManagerConfig public config;
34+
35+
/**
36+
* @dev Constructor initializes the contract with the owner, processor, and initial configuration.
37+
* @param _owner Address of the contract owner.
38+
* @param _processor Address of the processor that can execute functions.
39+
* @param _config Encoded configuration parameters for the MorphoVaultV1PositionManager.
40+
*/
41+
constructor(address _owner, address _processor, bytes memory _config) Library(_owner, _processor, _config) {}
42+
43+
/**
44+
* @notice Deposits assets to MorphoVaultV1.
45+
* @param amount The amount to deposit (0 for all available balance).
46+
*/
47+
function deposit(uint256 amount) external onlyProcessor {
48+
MorphoVaultV1PositionManagerConfig memory storedConfig = config;
49+
IERC20 asset = IERC20(storedConfig.assetAddress);
50+
51+
uint256 depositAmount = amount;
52+
if (amount == 0) {
53+
depositAmount = asset.balanceOf(address(storedConfig.inputAccount));
54+
}
55+
56+
require(depositAmount > 0, "No assets to deposit");
57+
58+
//Approve the MorphoVaultV1 to spend the base asset from the input account
59+
bytes memory encodedApproveCall = abi.encodeCall(IERC20.approve, (storedConfig.vaultAddress, depositAmount));
60+
61+
storedConfig.inputAccount.execute(address(asset), 0, encodedApproveCall);
62+
63+
// Deposit the base asset to the MorphoVaultV1
64+
bytes memory encodedDepositCall =
65+
abi.encodeCall(IERC4626.deposit, (depositAmount, address(storedConfig.inputAccount)));
66+
67+
storedConfig.inputAccount.execute(storedConfig.vaultAddress, 0, encodedDepositCall);
68+
}
69+
70+
/**
71+
* @notice Withdraws assets from MorphoVaultV1 position.
72+
* @param amount The amount to withdraw (0 to withdraw the entire balance).
73+
*/
74+
function withdraw(uint256 amount) external onlyProcessor {
75+
MorphoVaultV1PositionManagerConfig memory storedConfig = config;
76+
IMetaMorphoV1_1 vault = IMetaMorphoV1_1(storedConfig.vaultAddress);
77+
78+
uint256 withdrawAmount = amount;
79+
if (amount == 0) {
80+
withdrawAmount = vault.maxWithdraw(address(storedConfig.inputAccount));
81+
}
82+
83+
require(withdrawAmount > 0, "No vault tokens to withdraw");
84+
85+
// Withdraw from MorphoVaultV1 to output account
86+
bytes memory encodedWithdrawCall = abi.encodeCall(
87+
IERC4626.withdraw, (withdrawAmount, address(storedConfig.outputAccount), address(storedConfig.inputAccount))
88+
);
89+
90+
storedConfig.inputAccount.execute(storedConfig.vaultAddress, 0, encodedWithdrawCall);
91+
}
92+
93+
/**
94+
* @dev Internal initialization function called during construction
95+
* @param _config New configuration
96+
*/
97+
function _initConfig(bytes memory _config) internal override {
98+
config = validateConfig(_config);
99+
}
100+
101+
/**
102+
* @dev Updates the MorphoVaultV1PositionManager configuration.
103+
* Only the contract owner is authorized to call this function.
104+
* @param _config New encoded configuration parameters.
105+
*/
106+
function updateConfig(bytes memory _config) public override onlyOwner {
107+
// Validate and update the configuration.
108+
config = validateConfig(_config);
109+
}
110+
111+
/**
112+
* @notice Validates the provided configuration parameters
113+
* @dev Checks for validity of input account, output account, base asset, and market proxy address
114+
* @param _config The encoded configuration bytes to validate
115+
* @return MorphoVaultV1PositionManagerConfig A validated configuration struct
116+
*/
117+
function validateConfig(bytes memory _config) internal view returns (MorphoVaultV1PositionManagerConfig memory) {
118+
// Decode the configuration bytes into the MorphoVaultV1PositionManagerConfig struct.
119+
MorphoVaultV1PositionManagerConfig memory decodedConfig =
120+
abi.decode(_config, (MorphoVaultV1PositionManagerConfig));
121+
122+
// Ensure the Vault address is valid (non-zero).
123+
if (decodedConfig.vaultAddress == address(0)) {
124+
revert("Vault address can't be zero address");
125+
}
126+
127+
// Ensure the input account address is valid (non-zero).
128+
if (decodedConfig.inputAccount == BaseAccount(payable(address(0)))) {
129+
revert("Input account can't be zero address");
130+
}
131+
132+
// Ensure the output account address is valid (non-zero).
133+
if (decodedConfig.outputAccount == BaseAccount(payable(address(0)))) {
134+
revert("Output account can't be zero address");
135+
}
136+
137+
// Ensure the asset address is the same as the asset address of the vault
138+
if (decodedConfig.assetAddress != IMetaMorphoV1_1(decodedConfig.vaultAddress).asset()) {
139+
revert("Vault asset and given asset are not same");
140+
}
141+
142+
return decodedConfig;
143+
}
144+
145+
function balance() external view returns (uint256) {
146+
return IMetaMorphoV1_1(config.vaultAddress).previewRedeem(
147+
IMetaMorphoV1_1(config.vaultAddress).balanceOf(address(config.inputAccount))
148+
);
149+
}
150+
}

0 commit comments

Comments
 (0)