Skip to main content

Manage exposure

Be sure to have read the previous section and that you are aware of the different functions behavior.

Set the Supply Queue

Let's consider that someone applied all steps from the previous section such that the supplyQueue is thus composed of 3 market Ids:

0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41; // 0: wstETH/WETH(94.5%)
0x3c83f77bde9541f8d3d82533b19bbc1f97eb2f1098bb991728acbfbede09cc5d; // 1: rETH/WETH(94.5%)
0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284; // 2: Idle Market - WETH

One can verify it by putting 0, 1, 2, etc... numbers on the supplyQueue function on the vault itself.

To change the supplyQueue, one can call the setSupplyQueue with a new set of Id, by moving market 1 and 2 for instance:

0x3c83f77bde9541f8d3d82533b19bbc1f97eb2f1098bb991728acbfbede09cc5d; // 0: rETH/WETH(94.5%)
0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41; // 1: wstETH/WETH(94.5%)
0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284; // 2: Idle Market - WETH

And call the setSupplyQueue function with the object:

setSupplyQueue([0x3c...523,0xc5...c41,0x58...284])

Then one can verify that this worked as expected by querying again the supplyQueue function on the vault, one should see that the order changed.

Please keep in mind that it could be relevant to put the "idle" market as last element of the supplyQueue.

Update the Withdraw Queue

Let's assume a vault has the following withdrawQueue:

0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284; // 0: Idle Market - WETH
0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41; // 1: wstETH/WETH(94.5%)
0x3c83f77bde9541f8d3d82533b19bbc1f97eb2f1098bb991728acbfbede09cc5d; // 2: rETH/WETH(94.5%)

As a curator, if one wants to change the position of the last 2 markets:
-> call the function: updateWithdrawQueue with: ["0","2","1"].

The new withdrawQueue will be:

0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284; // 0: Idle Market - WETH
0x3c83f77bde9541f8d3d82533b19bbc1f97eb2f1098bb991728acbfbede09cc5d; // 1: rETH/WETH(94.5%)
0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41; // 2: wstETH/WETH(94.5%)

Please keep in mind that it could be relevant to put the "idle" market as first element of the withdrawQueue.

Remove a market

To force remove a market, one can call submitMarketRemoval.

Warning

  • Submits a forced market removal from the vault, eventually losing all funds supplied to the market.
  • Note that funds can be recovered by enabling this market again and withdrawing from it (using reallocate), but funds will be distributed pro-rata to the shares at the time of withdrawal, not at the time of removal.
  • This forced removal is expected to be used as an emergency process in case a market constantly reverts.
  • To softly remove a sane market, the curator role is expected to bundle a reallocation that empties the market first (using reallocate), followed by the removal of the market (using updateWithdrawQueue).
  • Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap wil prevent such reverts.

Let's assume a vault has the following withdrawQueue:

0x58e212060645d18eab6d9b2af3d56fbc906a92ff5667385f616f662c70372284; // 0: Idle Market - WETH
0x3c83f77bde9541f8d3d82533b19bbc1f97eb2f1098bb991728acbfbede09cc5d; // 1: rETH/WETH(94.5%)
0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41; // 2: wstETH/WETH(94.5%)

As a curator, if one wants to remove the last market, one can call the function: submitMarketRemoval with the marketParams of the market Id: 0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41. Here are the following steps that need to be done:

1. Retrieve the marketParams data

Jump into the Morpho contract, and in the read functions section, call the idToMarketParams function with the market Id you are expecting to remove as input:

input:
"0xc54d7acf14de29e0e5527cabd7a576506870346a78a11a6762e2cca66322ec41"; // wstETH/WETH(94.5%)

output:
"loanToken":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"collateralToken":"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"oracle":"0x2a01EB9496094dA03c4E364Def50f5aD1280AD72",
"irm":"0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC",
"lltv":"945000000000000000"

The value retrieved are the market parameters (marketParams) of the market you want to remove.

2. Send submitCap transaction

As the curator, send a submitCap transaction with a cap of 0 to the market you want to remove.
a. The marketParams one is expecting to remove, retrieved in 1.
b. The cap value, which has to be set to 0.


input:
[
[ // marketParams tuple
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"0x2a01EB9496094dA03c4E364Def50f5aD1280AD72",
"0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC",
"945000000000000000"
]
,0 // cap value, 0
]

3. Send submitMarketRemoval transaction

Paste again the marketParams retrieved in 1, and call the submitMarketRemoval function with those parameters:

[
"loanToken":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"collateralToken":"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0",
"oracle":"0x2a01EB9496094dA03c4E364Def50f5aD1280AD72",
"irm":"0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC",
"lltv":"945000000000000000"
]

4. Send an updateWithdrawQueue transaction

One wants to keep only the market of the withdrawQueue that are in position 0 and position 2 as per the example: After timelock (otherwise, the transaction will revert), put [0,2] in the updateWithdrawQueue function.

This is it, one can verify in the withdrawQueue that there no longer is the market that was removed.

Reallocation examples

Prerequisites

Any address that has the onlyAllocatorRole will be able to execute the reallocate function which reallocates the vault's liquidity to reach a given allocation of assets on each specified market.
The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan token of the market is the same as the vault's asset.

Be sure to have read the technical reference definition of the reallocate function before proceeding.

The function takes into input the MarketAllocation[], with MarketAllocation implemented like this:

struct MarketAllocation {
MarketParams marketParams;
uint256 assets;
}:

and MarketParams implemented like this:

struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}

The assets field represents the amount of liquidity you want to allocate to that specific market after the reallocation. It essentially defines the targeted state of your allocation.

Steps to Execute a Reallocation

  1. Identify the Target State: Determine how much of the vault's liquidity you want to allocate to each market. The total of these allocations should match the total liquidity available in the vault if you want to fully utilize the available funds.

  2. Prepare the MarketAllocations Array: For each market, create a MarketAllocation object with the appropriate parameters and the target amount of liquidity (assets) you want to allocate to that market.

  3. Execute the Reallocation: Call the reallocate function with the prepared MarketAllocations array.

Example

Here is an example of how to structure the MarketAllocation[] input for the reallocate function:

[
{
"marketParams": {
"loanToken": "0xLOAN_TOKEN_ADDRESS", // Address of the loan token
"collateralToken": "0xCOLLATERAL_TOKEN_ADDRESS", // Address of the collateral token
"oracle": "0xORACLE_ADDRESS", // Address of the oracle
"irm": "0xIRM_ADDRESS", // Address of the interest rate model
"lltv": "900000000000000000" // Loan-to-Value ratio, for example, 90% (18 decimals precision)
},
"assets": "TARGET_ASSET_AMOUNT" // Amount of assets to allocate
},
{
"marketParams": {
"loanToken": "0xLOAN_TOKEN_ADDRESS", // Address of the loan token
"collateralToken": "0xCOLLATERAL_TOKEN_ADDRESS", // Address of the collateral token
"oracle": "0xORACLE_ADDRESS", // Address of the oracle
"irm": "0xIRM_ADDRESS", // Address of the interest rate model
"lltv": "900000000000000000" // Loan-to-Value ratio, for example, 90% (18 decimals precision)
},
"assets": "TARGET_ASSET_AMOUNT" // Amount of assets to allocate
},
{
"marketParams": {
"loanToken": "0xLOAN_TOKEN_ADDRESS", // Address of the loan token
"collateralToken": "0x0000000000000000000000000000000000000000", // Use zero address for idle markets
"oracle": "0x0000000000000000000000000000000000000000", // Use zero address for idle markets
"irm": "0x0000000000000000000000000000000000000000", // Use zero address for idle markets
"lltv": "0"
},
"assets": "115792089237316195423570985008687907853269984665640564039457584007913129639935" // Max value to ensure all remaining assets are allocated
}
]
  1. To ensure that the total withdrawn amount equals the total supplied amount, the sender is expected to pass:
    assets = type(uint256).max in the last MarketAllocation of allocations. This guarantees that all the remaining withdrawn liquidity is supplied, ensuring totalWithdrawn = totalSupplied.
  2. If you expect a market to be empty, just remove it from the MarketAllocation[] array.