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 all 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
79 changes: 53 additions & 26 deletions src/PublicAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ import {
} from "../lib/metamorpho/src/interfaces/IMetaMorpho.sol";

import {MarketParamsLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/MarketParamsLib.sol";

import {MorphoBalancesLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";

import {Market} from "../lib/metamorpho/lib/morpho-blue/src/interfaces/IMorpho.sol";
import {UtilsLib} from "./libraries/UtilsLib.sol";

import {UtilsLib} from "../lib/metamorpho/lib/morpho-blue/src/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,
MAX_SETTABLE_FLOW_CAP,
IPublicAllocatorStaticTyping,
IPublicAllocatorBase
} from "./interfaces/IPublicAllocator.sol";

/// @title MetaMorpho
/// @author Morpho Labs
Expand All @@ -24,7 +32,6 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
using MorphoBalancesLib for IMorpho;
using MarketParamsLib for MarketParams;
using UtilsLib for uint256;
using UtilsLib for uint128;

/// CONSTANTS ///

Expand Down Expand Up @@ -72,36 +79,53 @@ 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);
Id depositMarketId = depositMarketParams.id();
uint128 totalWithdrawn;

for (uint256 i = 0; i < withdrawals.length; i++) {
Id id = withdrawals[i].marketParams.id();

// Revert if the market is elsewhere in the list, or is the deposit market.
for (uint256 j = i + 1; j < withdrawals.length; j++) {
if (Id.unwrap(id) == Id.unwrap(withdrawals[j].marketParams.id())) {
revert ErrorsLib.InconsistentWithdrawTo();
}
}
if (Id.unwrap(id) == Id.unwrap(depositMarketId)) revert ErrorsLib.InconsistentWithdrawTo();

uint128 withdrawnAssets = withdrawals[i].amount;
totalWithdrawn += withdrawnAssets;

MORPHO.accrueInterest(withdrawals[i].marketParams);
uint256 assets = MORPHO.expectedSupplyAssets(withdrawals[i].marketParams, address(VAULT));

allocations[i].marketParams = withdrawals[i].marketParams;
allocations[i].assets = assets - withdrawnAssets;
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
flowCap[id].maxIn += withdrawnAssets;
flowCap[id].maxOut -= withdrawnAssets;

emit EventsLib.PublicWithdrawal(id, withdrawnAssets);
}

allocations[withdrawals.length].marketParams = depositMarketParams;
allocations[withdrawals.length].assets = type(uint256).max;
flowCap[depositMarketId].maxIn -= totalWithdrawn;
flowCap[depositMarketId].maxOut += totalWithdrawn;

VAULT.reallocate(allocations);

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;
}
if (MORPHO.expectedSupplyAssets(depositMarketParams, address(VAULT)) > supplyCap[depositMarketId]) {
revert ErrorsLib.PublicAllocatorSupplyCapExceeded(depositMarketId);
}

emit EventsLib.PublicReallocate(msg.sender);
emit EventsLib.PublicReallocateTo(msg.sender, depositMarketId, totalWithdrawn);
}

/// OWNER ONLY ///
Expand All @@ -123,6 +147,9 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
/// @inheritdoc IPublicAllocatorBase
function setFlowCaps(FlowConfig[] calldata flowCaps) external onlyOwner {
for (uint256 i = 0; i < flowCaps.length; i++) {
if (flowCaps[i].cap.maxIn > MAX_SETTABLE_FLOW_CAP || flowCaps[i].cap.maxOut > MAX_SETTABLE_FLOW_CAP) {
revert ErrorsLib.MaxSettableFlowCapExceeded();
}
flowCap[flowCaps[i].id] = flowCaps[i].cap;
}

Expand Down
32 changes: 27 additions & 5 deletions src/interfaces/IPublicAllocator.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
// 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";

/// @dev Equal to type(uint128).max/2, so flow caps can always be stored on 128 bits
/// @dev The actual max possible flow cap is type(uint128).max-1.
uint128 constant MAX_SETTABLE_FLOW_CAP = 170141183460469231731687303715884105727;

struct FlowCap {
/// @notice The maximum allowed inflow in a market
Expand All @@ -20,6 +30,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 +48,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 All @@ -51,6 +72,7 @@ interface IPublicAllocatorBase {
function transferFee(address payable feeRecipient) external;

/// @notice Sets the maximum inflow and outflow through public allocation for some markets.
/// @dev Max allowed inflow/outflow is MAX_SETTABLE_FLOW_CAP.
/// @dev Doesn't revert if it doesn't change the storage at all.
function setFlowCaps(FlowConfig[] calldata _flowCaps) external;

Expand Down
9 changes: 8 additions & 1 deletion src/libraries/ErrorsLib.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

/// @title ErrorsLib
/// @author Morpho Labs
Expand Down Expand Up @@ -38,6 +39,12 @@ library ErrorsLib {
/// @notice Thrown when the value is already set.
error AlreadySet();

/// @notice Thrown when there are duplicates with nonzero assets in `withdrawTo` arguments.
error InconsistentWithdrawTo();

/// @notice Thrown when attempting to set max inflow/outflow above the MAX_SETTABLE_FLOW_CAP.
error MaxSettableFlowCapExceeded();

/// @notice Thrown when the PublicAllocatorFactory is called with a vault not made by the MetaMorphoFactory.
error NotMetaMorpho();
}
11 changes: 7 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 security@morpho.org
/// @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 Down
26 changes: 0 additions & 26 deletions src/libraries/UtilsLib.sol

This file was deleted.

File renamed without changes.
Loading