Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 33 additions & 0 deletions .changeset/shared-liquidity-target-utilization-90.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
"@morpho-org/morpho-sdk": minor
"@morpho-org/liquidity-sdk-viem": minor
"@morpho-org/wdk-protocol-lending-morpho-evm": patch
---

Default the shared-liquidity target utilization to 90% and deprecate all
customization of it. This is non-breaking: the tuning surface stays in place and
explicit overrides are still honored until the next major.

**`@morpho-org/morpho-sdk`**:

- `DEFAULT_SUPPLY_TARGET_UTILIZATION` and `DEFAULT_WITHDRAWAL_TARGET_UTILIZATION`
are now both 90% (previously 90.5% and 92%). Callers that pass no explicit
override now trigger reallocations at 90% and cap phase-1 source-market
withdrawals at 90%. The aggressive fallback still drains to 100% as a last resort.
- `PublicAllocatorOptions.maxWithdrawalUtilization` /
`defaultMaxWithdrawalUtilization` and
`ReallocationComputeOptions.supplyTargetUtilization` /
`defaultSupplyTargetUtilization` are now `@deprecated`. They remain fully
functional and will be removed in the next major.

**`@morpho-org/liquidity-sdk-viem`**:

- `LiquidityParameters` and the `LiquidityLoader` `parameters` constructor
argument are now `@deprecated`. The source-market withdrawal ceiling defaults to
90% and the Morpho API's `targetWithdrawUtilization` field is no longer
consulted; explicitly passed `parameters` overrides are still honored until the
next major.
- The `@morpho-org/morpho-sdk` peer range moves to `^5.4.0`: the 90% default
ceiling lives in `morpho-sdk`'s `DEFAULT_WITHDRAWAL_TARGET_UTILIZATION`, so an
older peer would silently fall back to the previous 92% default now that the
API value is no longer consulted.
2 changes: 1 addition & 1 deletion packages/liquidity-sdk-viem/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"peerDependencies": {
"@morpho-org/blue-sdk": "^6.0.0",
"@morpho-org/blue-sdk-viem": "^5.0.0",
"@morpho-org/morpho-sdk": "^5.0.0",
"@morpho-org/morpho-sdk": "^5.4.0",
"@morpho-org/morpho-ts": "^2.7.0",
"dataloader": "^2.2.3",
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
Expand Down
12 changes: 9 additions & 3 deletions packages/liquidity-sdk-viem/src/loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,25 +633,31 @@ describe.sequential("LiquidityLoader.fetch", () => {
]);
});

test("uses caller-provided withdrawal utilization options", async () => {
test("caps source withdrawals at the default 90% utilization, ignoring the API value", async () => {
// The API advertises a 100% source ceiling, but it is no longer consulted:
// the source sits at 90% utilization (900 borrow / 1000 supply) and the
// default 90% ceiling lets nothing be pulled. If the API's 100% were still
// read, 100 assets would be withdrawable — so `[]` proves it is ignored.
const cappedHandle = setupLoaderMockClient({
blockTimestamp: 100n,
targetPendingCapValue: 10_000n,
sourceBorrowAssets: 900n,
});
mockApiMarkets(900000000000000000n);
mockApiMarkets(MathLib.WAD);

const capped = await new LiquidityLoader(cappedHandle.client).fetch(
targetMarketId,
);
expect(capped.withdrawals).toStrictEqual([]);

// The deprecated `parameters.maxWithdrawalUtilization` override is still
// honored: forcing 100% drains the source down to that utilization.
const overrideHandle = setupLoaderMockClient({
blockTimestamp: 100n,
targetPendingCapValue: 10_000n,
sourceBorrowAssets: 900n,
});
mockApiMarkets(900000000000000000n);
mockApiMarkets(MathLib.WAD);

const override = await new LiquidityLoader(overrideHandle.client, {
maxWithdrawalUtilization: {
Expand Down
35 changes: 21 additions & 14 deletions packages/liquidity-sdk-viem/src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@ import { getBlock } from "viem/actions";
import { apiSdk } from "./api/index.js";

const REALLOCATION_SIMULATION_DELAY = 3_600n;
/**
* Optional tuning for the shared-liquidity source-market withdrawal ceiling.
*
* @deprecated The source-market withdrawal ceiling is fixed at 90%
* (`DEFAULT_WITHDRAWAL_TARGET_UTILIZATION` in `@morpho-org/morpho-sdk`) and will
* stop being configurable in the next major. Overrides are still honored for now.
*/
export interface LiquidityParameters {
/**
* The default maximum utilization allowed to reach to find shared liquidity (scaled by WAD).
*
* @deprecated Fixed at 90% and will be removed in the next major.
*/
defaultMaxWithdrawalUtilization?: bigint;

/**
* If provided, defines the maximum utilization allowed to reach for each market, defaulting to `defaultMaxWithdrawalUtilization`.
* If not, these values are fetched from Morpho API.
*
* @deprecated Fixed at 90% and will be removed in the next major. The Morpho
* API's `targetWithdrawUtilization` is no longer consulted.
*/
maxWithdrawalUtilization?: Record<MarketId, bigint>;
}
Expand All @@ -40,6 +51,10 @@ export class LiquidityLoader<chain extends Chain = Chain> {

constructor(
public client: Client<Transport, chain>,
/**
* @deprecated The source-market withdrawal ceiling is fixed at 90% and will
* stop being configurable in the next major. Overrides are still honored for now.
*/
public readonly parameters: LiquidityParameters = {},
) {
this.dataLoader = new DataLoader(
Expand Down Expand Up @@ -170,25 +185,17 @@ export class LiquidityLoader<chain extends Chain = Chain> {
),
});

const maxWithdrawalUtilization =
parameters.maxWithdrawalUtilization ??
fromEntries(
// biome-ignore lint/suspicious/noShadow: TODO rename to avoid shadowing
allVaultsMarkets.flatMap(([, markets]) =>
markets.map((market) => [
market.uniqueKey,
market.targetWithdrawUtilization,
]),
),
);

return apiMarkets.map(({ uniqueKey, targetBorrowUtilization }) => {
try {
// The source-market withdrawal ceiling defaults to 90%
// (DEFAULT_WITHDRAWAL_TARGET_UTILIZATION) inside
// `getMarketPublicReallocations`; the API's per-market
// `targetWithdrawUtilization` is no longer consulted. Deprecated
// `parameters` overrides are still forwarded for backward compatibility.
const { data: endState, withdrawals } =
startState.getMarketPublicReallocations(uniqueKey, {
...parameters,
timestamp: block.timestamp + REALLOCATION_SIMULATION_DELAY,
maxWithdrawalUtilization,
enabled: true,
});

Expand Down
59 changes: 23 additions & 36 deletions packages/liquidity-sdk-viem/test/loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,10 @@ describe("dataloader", () => {

expect(reallocations).toStrictEqual([
{
assets: 3873700460362n,
assets: 1609457675962n,
id: "0x3a85e619751152991742810df6ec69ce473daef99e28a64ab2340d7b7ccfee49",
vault: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
},
{
assets: 1511034176993n,
id: "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64",
vault: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
},
{
assets: 89n,
id: "0x3bb29b62affbedc60b8446b235aaa349d5e3bad96c09bca1d7a2d693c06669aa",
vault: "0x186514400e52270cef3D80e1c6F8d10A75d47344",
},
]);
});

Expand All @@ -52,62 +42,59 @@ describe("dataloader", () => {

expect(eth_reallocations).toStrictEqual([
{
assets: 1275140484727340602998n,
assets: 1159397680152036107732n,
id: "0xb8fc70e82bc5bb53e773626fcc6a23f7eefa036918d7ef216ecfb1950a94a85e",
vault: "0x2371e134e3455e0593363cBF89d3b6cf53740618",
},
{
assets: 365464355757324173961n,
assets: 334181181282594142674n,
id: "0xba761af4134efb0855adfba638945f454f0a704af11fc93439e20c7c5ebab942",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 228469609053469911383n,
id: "0xd0e50cdac92fe2172043f5e0c36532c6369d24947e40968f34a5e8819ca9ec5d",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 213832870661212422340n,
id: "0x2287407f0f42ad5ad224f70e4d9da37f02770f79959df703d6cfee8afc548e0d",
vault: "0x78Fc2c2eD1A4cDb5402365934aE5648aDAd094d0",
},
{
assets: 124911026994913541789n,
assets: 119531849162836164920n,
id: "0xcacd4c39af872ddecd48b650557ff5bcc7d3338194c0f5b2038e0d4dec5dc022",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 34067768940166463160n,
id: "0x138eec0e4a1937eb92ebc70043ed539661dd7ed5a89fb92a720b341650288a40",
assets: 84374938643006667977n,
id: "0xd0e50cdac92fe2172043f5e0c36532c6369d24947e40968f34a5e8819ca9ec5d",
vault: "0x2371e134e3455e0593363cBF89d3b6cf53740618",
},
{
assets: 23607619147993488117n,
id: "0x37e7484d642d90f14451f1910ba4b7b8e4c3ccdd0ec28f8b2bdb35479e472ba7",
assets: 66930590311663470057n,
id: "0x0eed5a89c7d397d02fd0b9b8e42811ca67e50ed5aeaa4f22e506516c716cfbbf",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 12852204041462170816n,
id: "0xa0534c78620867b7c8706e3b6df9e69a2bc67c783281b7a77e034ed75cee012e",
assets: 23306805022282394159n,
id: "0x698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a115",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 240388238484242n,
id: "0x87a3e5dbcd822f2a543bea1365b7dd99ad9a1cb460061278319732e63207c792",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
assets: 14167222149320089956n,
id: "0x138eec0e4a1937eb92ebc70043ed539661dd7ed5a89fb92a720b341650288a40",
vault: "0x2371e134e3455e0593363cBF89d3b6cf53740618",
},
]);
expect(usdc_reallocations).toStrictEqual([
{
assets: 2520842846565n,
id: "0xb323495f7e4148be5643a4ea4a8221eef163e4bccfdedc2a6f4696baacbc86cc",
vault: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
assets: 10242440491887459673n,
id: "0xa0534c78620867b7c8706e3b6df9e69a2bc67c783281b7a77e034ed75cee012e",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
{
assets: 1511034176993n,
id: "0x64d65c9a2d91c36d56fbc42d69e979335320169b3df63bf92789e2c8883fcc64",
vault: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB",
assets: 348595829549040913n,
id: "0xea023e57814fb9a814a5a9ee9f3e7ece5b771dd8cc703e50b911e9cde064a12d",
vault: "0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658",
},
]);
// At the pinned block, every usdc_wbtc source market already sits at or
// above 90% utilization, so the default 90% withdrawal ceiling (previously
// the API's per-market targetWithdrawUtilization) yields no withdrawals.
expect(usdc_reallocations).toStrictEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ReallocationData } from "./reallocationData.js";
const VAULT_A: Address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const TIMESTAMP = 1_700_000_000n;
const NINETY_PERCENT = (9n * MathLib.WAD) / 10n;
const EIGHTY_PERCENT = (8n * MathLib.WAD) / 10n;

const targetParams = new MarketParams(WethUsdsBlue);
const sourceParamsA = new MarketParams(CbbtcUsdcBlue);
Expand Down Expand Up @@ -120,19 +121,20 @@ describe("ReallocationData.getAvailableLiquidityToUtilization", () => {
).toBe(580n * MathLib.WAD);
});

test("behavior: returns only own headroom when supplyTargetUtilization > utilization", () => {
// Default supplyTarget (90.5%) > target (90%) → reallocation would not
// trigger, so available liquidity is excluded: ownHeadroom = 400.
test("behavior: returns only own headroom when the supply target exceeds the queried utilization", () => {
// Default supply target (90%) > queried utilization (80%) → reallocation
// would not trigger, so available liquidity is excluded: ownHeadroom to 80% =
// 1000·0.8 − 500 = 300.
const data = makeData();
stubReallocations(data, [
{ id: sourceParamsA.id, vault: VAULT_A, assets: 200n * MathLib.WAD },
]);

expect(
data.getAvailableLiquidityToUtilization(targetParams.id, NINETY_PERCENT, {
data.getAvailableLiquidityToUtilization(targetParams.id, EIGHTY_PERCENT, {
timestamp: TIMESTAMP,
}),
).toBe(400n * MathLib.WAD);
).toBe(300n * MathLib.WAD);
});

test("behavior: returns only scaled available liquidity when utilization equals current utilization", () => {
Expand Down
9 changes: 8 additions & 1 deletion packages/morpho-sdk/src/entities/reallocationData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SimulationState as SimulationStateImport } from "@morpho-org/simulation
import type { Address } from "viem";
import { maxUint256, zeroAddress } from "viem";
import { describe, expect, test } from "vitest";
import { DEFAULT_WITHDRAWAL_TARGET_UTILIZATION } from "../helpers/constant.js";
import {
DisabledReallocationMarketError,
MissingPublicAllocatorConfigError,
Expand Down Expand Up @@ -342,7 +343,13 @@ const toLegacyOptions = (
options.reallocatableVaults == null
? undefined
: [...options.reallocatableVaults],
defaultMaxWithdrawalUtilization: options.defaultMaxWithdrawalUtilization,
// The legacy `SimulationState` reference defaults its withdrawal ceiling to
// 92% (frozen in `@morpho-org/simulation-sdk`); morpho-sdk now defaults to 90%.
// Pin the reference to the SDK default when a case sets none, so the parity
// comparison stays algorithm-only (not default-driven).
defaultMaxWithdrawalUtilization:
options.defaultMaxWithdrawalUtilization ??
DEFAULT_WITHDRAWAL_TARGET_UTILIZATION,
maxWithdrawalUtilization:
options.maxWithdrawalUtilization == null
? undefined
Expand Down
14 changes: 10 additions & 4 deletions packages/morpho-sdk/src/helpers/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ export const DEFAULT_LLTV_BUFFER = MathLib.WAD / 200n;
export const MAX_ABSOLUTE_SHARE_PRICE = 100n * MathLib.RAY;

/**
* The default maximum utilization allowed to reach when withdrawing shared liquidity.
* The default maximum utilization a source market may reach when withdrawing
* shared liquidity, scaled by WAD. Still overridable through the deprecated
* `maxWithdrawalUtilization` / `defaultMaxWithdrawalUtilization` options until
* the next major.
*/
export const DEFAULT_WITHDRAWAL_TARGET_UTILIZATION = 92_0000000000000000n;
export const DEFAULT_WITHDRAWAL_TARGET_UTILIZATION = 90_0000000000000000n;

/**
* The default target utilization above which shared liquidity reallocations are triggered.
* The default target utilization above which shared liquidity reallocations are
* triggered (and the level the target market is brought back to), scaled by WAD.
* Still overridable through the deprecated `supplyTargetUtilization` /
* `defaultSupplyTargetUtilization` options until the next major.
*/
export const DEFAULT_SUPPLY_TARGET_UTILIZATION = 90_5000000000000000n;
export const DEFAULT_SUPPLY_TARGET_UTILIZATION = 90_0000000000000000n;

/**
* Tokens that require setting allowance from zero before changing approval.
Expand Down
20 changes: 18 additions & 2 deletions packages/morpho-sdk/src/types/sharedLiquidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export interface PublicAllocatorOptions {
/**
* The maximum utilization each source market may reach when withdrawing
* shared liquidity, scaled by WAD.
*
* @deprecated The source-market withdrawal ceiling is fixed at 90%
* ({@link DEFAULT_WITHDRAWAL_TARGET_UTILIZATION}) and will stop being
* configurable in the next major. Per-market overrides are still honored for now.
*/
readonly maxWithdrawalUtilization?: Readonly<
Record<MarketId, bigint | undefined>
Expand All @@ -31,7 +35,11 @@ export interface PublicAllocatorOptions {
/**
* The default maximum utilization source markets may reach when withdrawing
* shared liquidity, scaled by WAD.
* @default 92% (920000000000000000n)
*
* @default 90% (900000000000000000n)
* @deprecated The source-market withdrawal ceiling is fixed at 90%
* ({@link DEFAULT_WITHDRAWAL_TARGET_UTILIZATION}) and will stop being
* configurable in the next major. Overrides are still honored for now.
*/
readonly defaultMaxWithdrawalUtilization?: bigint;
}
Expand Down Expand Up @@ -84,6 +92,10 @@ export interface ReallocationComputeOptions extends PublicAllocatorOptions {
* Per-market target utilization above which the shared liquidity algorithm
* is triggered (scaled by WAD). Overrides `defaultSupplyTargetUtilization`
* for the specified market.
*
* @deprecated The supply-target trigger is fixed at 90%
* ({@link DEFAULT_SUPPLY_TARGET_UTILIZATION}) and will stop being configurable
* in the next major. Per-market overrides are still honored for now.
*/
readonly supplyTargetUtilization?: Readonly<
Record<MarketId, bigint | undefined>
Expand All @@ -92,7 +104,11 @@ export interface ReallocationComputeOptions extends PublicAllocatorOptions {
/**
* The default target utilization above which the shared liquidity algorithm
* is triggered (scaled by WAD).
* @default 90.5% (905000000000000000n)
*
* @default 90% (900000000000000000n)
* @deprecated The supply-target trigger is fixed at 90%
* ({@link DEFAULT_SUPPLY_TARGET_UTILIZATION}) and will stop being configurable
* in the next major. Overrides are still honored for now.
*/
readonly defaultSupplyTargetUtilization?: bigint;
}