Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reallocate by withdrawals + sink #5

Merged
merged 42 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3473c5d
feat: reallocate by withdrawals + sink
adhusson Feb 13, 2024
8048f5e
chore: remove dead code/comment
adhusson Feb 13, 2024
f11432d
fix: add back fee check
adhusson Feb 13, 2024
79cebbf
fix: add back reallocate call + cap check
adhusson Feb 13, 2024
ca196c5
test: adapt tests
adhusson Feb 13, 2024
09793b5
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 13, 2024
4e137bf
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 14, 2024
9025853
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 15, 2024
22c075b
fmt: forge fmt
adhusson Feb 15, 2024
a510cf1
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 15, 2024
a09243a
feat: add withdrawTo-specific events
adhusson Feb 15, 2024
1fd4621
fmt: fmt
adhusson Feb 15, 2024
94d3fcc
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 15, 2024
8b616ae
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 15, 2024
3f0ea62
fix: protect against duplicates with nonzero assets in withdrawTo arg…
adhusson Feb 15, 2024
58d8b1d
chore: remove console2 import
adhusson Feb 15, 2024
0b285d5
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 16, 2024
8e391ad
doc: natspec
adhusson Feb 16, 2024
a12f860
fix: canonical loop increment form
adhusson Feb 16, 2024
172f02c
feat: PublicAllocatorFactory, restricted to vaults created by a MetaM…
adhusson Feb 16, 2024
9c28448
feat: safe max value for flow caps
adhusson Feb 16, 2024
40e2700
Merge branch 'feat/reallocate-by-flow' into fix/duplicates-in-realloc…
adhusson Feb 16, 2024
4003542
chore: fmt
MathisGD Feb 16, 2024
2d69ebc
docs: minor fix
MathisGD Feb 16, 2024
ea4d87f
fix: small compilation issues
QGarchery Feb 16, 2024
366493a
Merge pull request #10 from morpho-org/fix/small-issues-duplicates
QGarchery Feb 16, 2024
fc3c880
feat: max settable flow cap as an integer
QGarchery Feb 16, 2024
c5a55f7
Merge pull request #9 from morpho-org/feat/safe-max-flow-cap
adhusson Feb 16, 2024
ce71ee7
refactor: rename depositAssets -> depositMarketAssets
adhusson Feb 16, 2024
9b75c1c
refactor: alternative fix for duplicates
MathisGD Feb 16, 2024
96443b7
rename depositMarketAssets -> vaultSupplyInMarket
adhusson Feb 16, 2024
102b1eb
Merge branch 'feat/reallocate-by-flow' into fix/duplicates-in-realloc…
adhusson Feb 16, 2024
e63d942
Merge branch 'feat/flows-and-eth-fee' into feat/reallocate-by-flow
adhusson Feb 16, 2024
87d45ff
Merge branch 'feat/reallocate-by-flow' into fix/duplicates-in-realloc…
adhusson Feb 16, 2024
bc945f4
Merge branch 'fix/duplicates-in-reallocate-by-flow' into fix/duplicat…
adhusson Feb 16, 2024
eb9eeb4
feat: accrue interest before computing expect supply assets
adhusson Feb 16, 2024
2ff7a7f
Merge remote-tracking branch 'origin/feat/flows-and-eth-fee' into fea…
MathisGD Feb 16, 2024
fbdcf06
Merge remote-tracking branch 'origin/feat/reallocate-by-flow' into fe…
MathisGD Feb 16, 2024
86fd595
feat: remove clamp
MathisGD Feb 16, 2024
d9bbfc5
Merge branch 'feat/reallocate-by-flow' into fix/duplicates-2
MathisGD Feb 16, 2024
de0022a
chore: fmt
MathisGD Feb 16, 2024
cfadc41
Merge pull request #11 from morpho-org/fix/duplicates-2
MathisGD Feb 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/metamorpho
Submodule metamorpho updated 0 files
64 changes: 40 additions & 24 deletions src/PublicAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ import {UtilsLib} from "./libraries/UtilsLib.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import {FlowCap, FlowConfig, SupplyConfig, IPublicAllocatorStaticTyping, IPublicAllocatorBase} from "./interfaces/IPublicAllocator.sol";
import {
FlowCap,
FlowConfig,
SupplyConfig,
Withdrawal,
IPublicAllocatorStaticTyping,
IPublicAllocatorBase
} from "./interfaces/IPublicAllocator.sol";

/// @title MetaMorpho
/// @author Morpho Labs
Expand Down Expand Up @@ -74,36 +81,45 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
/// PUBLIC ///

/// @inheritdoc IPublicAllocatorBase
function reallocate(MarketAllocation[] calldata allocations) external payable {
function withdrawTo(Withdrawal[] calldata withdrawals, MarketParams calldata depositMarketParams)
external
payable
{
if (msg.value != fee) revert ErrorsLib.IncorrectFee();

uint256[] memory assets = new uint256[](allocations.length);
for (uint256 i = 0; i < allocations.length; i++) {
// Do not compute interest twice for every market
MORPHO.accrueInterest(allocations[i].marketParams);
assets[i] = MORPHO.expectedSupplyAssets(allocations[i].marketParams, address(VAULT));
}
MarketAllocation[] memory allocations = new MarketAllocation[](withdrawals.length + 1);
allocations[withdrawals.length].marketParams = depositMarketParams;
allocations[withdrawals.length].assets = type(uint256).max;

VAULT.reallocate(allocations);
uint128 totalWithdrawn;

MarketParams memory marketParams;
for (uint256 i = 0; i < allocations.length; i++) {
marketParams = allocations[i].marketParams;
Id id = marketParams.id();
uint256 newAssets = MORPHO.expectedSupplyAssets(marketParams, address(VAULT));
if (newAssets >= assets[i]) {
if (newAssets > supplyCap[id]) revert ErrorsLib.PublicAllocatorSupplyCapExceeded(id);
uint128 inflow = (newAssets - assets[i]).toUint128();
flowCap[id].maxIn -= inflow;
flowCap[id].maxOut = (flowCap[id].maxOut).saturatingAdd(inflow);
} else {
uint128 outflow = (assets[i] - newAssets).toUint128();
flowCap[id].maxIn = (flowCap[id].maxIn).saturatingAdd(outflow);
flowCap[id].maxOut -= outflow;
for (uint256 i = 0; i < withdrawals.length; i++) {
allocations[i].marketParams = withdrawals[i].marketParams;
Id id = withdrawals[i].marketParams.id();
uint256 assets = MORPHO.expectedSupplyAssets(withdrawals[i].marketParams, address(VAULT));
adhusson marked this conversation as resolved.
Show resolved Hide resolved
uint128 withdrawnAssets = withdrawals[i].amount;
// Clamp at 0 if withdrawnAssets is too big
if (withdrawnAssets > assets) {
withdrawnAssets = assets.toUint128();
}
MathisGD marked this conversation as resolved.
Show resolved Hide resolved

totalWithdrawn += withdrawnAssets;
allocations[i].assets = assets - withdrawnAssets;
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
flowCap[id].maxIn = (flowCap[id].maxIn).saturatingAdd(withdrawnAssets);
flowCap[id].maxOut -= withdrawnAssets;
emit EventsLib.PublicWithdrawal(id, withdrawnAssets);
}

emit EventsLib.PublicReallocate(msg.sender);
VAULT.reallocate(allocations);

Id depositMarketId = depositMarketParams.id();
uint256 depositAssets = MORPHO.expectedSupplyAssets(depositMarketParams, address(VAULT));
QGarchery marked this conversation as resolved.
Show resolved Hide resolved
adhusson marked this conversation as resolved.
Show resolved Hide resolved
if (depositAssets > supplyCap[depositMarketId]) {
revert ErrorsLib.PublicAllocatorSupplyCapExceeded(depositMarketId);
}
flowCap[depositMarketId].maxIn -= totalWithdrawn;
flowCap[depositMarketId].maxOut = (flowCap[depositMarketId].maxOut).saturatingAdd(totalWithdrawn);
emit EventsLib.PublicReallocateTo(msg.sender, depositMarketId, totalWithdrawn);
}

/// OWNER ONLY ///
Expand Down
55 changes: 55 additions & 0 deletions src/PublicAllocatorFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.24;

import {IPublicAllocator} from "./interfaces/IPublicAllocator.sol";
import {IPublicAllocatorFactory} from "./interfaces/IPublicAllocatorFactory.sol";
import {IMetaMorphoFactory} from "../lib/metamorpho/src/interfaces/IMetaMorphoFactory.sol";

import {EventsLib} from "./libraries/EventsLib.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";

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

/// @title PublicAllocatorFactory
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice This contract allows to create public allocators for MetaMorpho vaults, and to index them easily.
contract PublicAllocatorFactory is IPublicAllocatorFactory {
/* IMMUTABLES */

/// @inheritdoc IPublicAllocatorFactory
IMetaMorphoFactory public immutable METAMORPHO_FACTORY;

/* STORAGE */

/// @inheritdoc IPublicAllocatorFactory
mapping(address => bool) public isPublicAllocator;

/* CONSTRUCTOR */

/// @dev Initializes the contract.
/// @param metaMorphoFactory The address of the MetaMorpho Factory.
constructor(address metaMorphoFactory) {
if (metaMorphoFactory == address(0)) revert ErrorsLib.ZeroAddress();

METAMORPHO_FACTORY = IMetaMorphoFactory(metaMorphoFactory);
}

/* EXTERNAL */

/// @inheritdoc IPublicAllocatorFactory
function createPublicAllocator(address initialOwner, address vault, bytes32 salt)
external
returns (IPublicAllocator publicAllocator)
{
if (!METAMORPHO_FACTORY.isMetaMorpho(vault)) {
revert ErrorsLib.NotMetaMorpho();
}

publicAllocator = IPublicAllocator(address(new PublicAllocator{salt: salt}(initialOwner, vault)));

isPublicAllocator[address(publicAllocator)] = true;

emit EventsLib.CreatePublicAllocator(address(publicAllocator), msg.sender, initialOwner, vault, salt);
}
}
27 changes: 22 additions & 5 deletions src/interfaces/IPublicAllocator.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.21;

import {IMetaMorpho, IMorpho, MarketAllocation, Id} from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol";
import {
IMetaMorpho,
IMorpho,
MarketAllocation,
Id,
MarketParams
} from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol";

struct FlowCap {
/// @notice The maximum allowed inflow in a market
Expand All @@ -20,6 +26,13 @@ struct SupplyConfig {
uint256 cap;
}

struct Withdrawal {
/// @notice The market from which to withdraw.
MarketParams marketParams;
/// @notice The amount to withdraw.
uint128 amount;
}

/// @dev This interface is used for factorizing IPublicAllocatorStaticTyping and IPublicAllocator.
/// @dev Consider using the IPublicAllocator interface instead of this one.
interface IPublicAllocatorBase {
Expand All @@ -31,18 +44,22 @@ interface IPublicAllocatorBase {

/// @notice The address of the Morpho contract.
function MORPHO() external view returns (IMorpho);

/// @notice The current fee.
function fee() external view returns (uint256);

/// @notice Given a market, the cap a supply through public allocation cannot exceed.
/// @notice A withdraw through public allocation can start and end above the cap.
function supplyCap(Id) external view returns (uint256);

/// @notice Calls the vault's `reallocate` function.
/// @notice See MetaMorpho's `reallocate` function documentation.
/// @notice Reallocate from a list of markets to one market.
/// @param withdrawals The markets to withdraw from,and the amounts to withdraw.
/// @param depositMarketParams The market receiving total withdrawn to.
/// @dev Will call MetaMorpho's `reallocate`.
/// @dev Checks that the public allocator constraints are respected.
function reallocate(MarketAllocation[] calldata allocations) external payable;
function withdrawTo(Withdrawal[] calldata withdrawals, MarketParams calldata depositMarketParams)
external
payable;

/// @notice Set the current fee.
function setFee(uint256 _fee) external;
Expand Down
26 changes: 26 additions & 0 deletions src/interfaces/IPublicAllocatorFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IPublicAllocator} from "./IPublicAllocator.sol";
import {IMetaMorphoFactory} from "../../lib/metamorpho/src/interfaces/IMetaMorphoFactory.sol";

/// @title IPublicAllocatorFactory
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface of PublicAllocator's factory.
interface IPublicAllocatorFactory {
/// @notice The address of the MetaMorphoFactory.
function METAMORPHO_FACTORY() external view returns (IMetaMorphoFactory);

/// @notice Whether an address is a PublicAllocator created by the factory.
function isPublicAllocator(address target) external view returns (bool);

/// @notice Creates a new PublicAllocator.
/// @param initialOwner The owner of the vault.
/// @param vault The vault the allocator will be attached to.
/// @param salt The salt to use for the MetaMorpho vault's CREATE2 address.
/// @dev Will only create public allocators for vault created by METAMORPHO_FACTORY.
function createPublicAllocator(address initialOwner, address vault, bytes32 salt)
external
returns (IPublicAllocator publicAllocator);
}
3 changes: 3 additions & 0 deletions src/libraries/ErrorsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ library ErrorsLib {

/// @notice Thrown when the value is already set.
error AlreadySet();

/// @notice Thrown when the PublicAllocatorFactory is called with a vault not made by the MetaMorphoFactory.
error NotMetaMorpho();
}
21 changes: 17 additions & 4 deletions src/libraries/EventsLib.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {FlowConfig, SupplyConfig} from "../interfaces/IPublicAllocator.sol";
import {FlowConfig, SupplyConfig, Id} from "../interfaces/IPublicAllocator.sol";

/// @title EventsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing events.
library EventsLib {
/// @notice Emitted when the public reallocation is triggered.
event PublicReallocate(address sender);
/// @notice Emitted during a public reallocation for each withdrawn-from market.
event PublicWithdrawal(Id id, uint256 withdrawnAssets);

/// @notice Emitted when the owner changes the `fee`.
/// @notice Emitted at the end of a public reallocation.
event PublicReallocateTo(address sender, Id depositMarketId, uint256 depositedAssets);

MathisGD marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Emitted when the owner changes the `fee`
event SetFee(uint256 fee);

/// @notice Emitted when the owner transfers the fee.
Expand All @@ -22,4 +25,14 @@ library EventsLib {

/// @notice Emitted when the owner updates some supply caps.
event SetSupplyCaps(SupplyConfig[] supplyCaps);

/// @notice Emitted when a new PublicAllocator is created.
/// @param publicAllocator The address of the created PublicAllocator.
/// @param caller The caller of the function.
/// @param initialOwner The initial owner of the PublicAllocator.
/// @param vault The MetaMorpho vault attached to the PublicAllocator.
/// @param salt The salt used for the PublicAllocator's CREATE2 address.
event CreatePublicAllocator(
address indexed publicAllocator, address indexed caller, address initialOwner, address vault, bytes32 salt
);
}
Loading