From feb22102f614b50e85ad7830051421ae39f90fed Mon Sep 17 00:00:00 2001 From: Foulk plb | Morpho <71005796+Foulks-Plb@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:43:27 +0200 Subject: [PATCH 1/2] feat(blue-sdk): accrue nested Vault V2 entities to a single timestamp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AccrualVaultV2.accrueInterest now accrues every underlying adapter, market, and position to the same timestamp, so the returned entity graph shares one `lastUpdate` — matching MetaMorpho V1 (AccrualVault.accrueInterest) instead of leaving nested adapters at pre-accrual state. Adds an additive `accrueInterest(timestamp?)` method to IAccrualVaultV2Adapter and each adapter. Vault-level `_totalAssets`, `totalSupply`, and fee shares are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/vault-v2-accrue-nested-entities.md | 8 ++ .../blue-sdk/src/vault/v2/VaultV2.test.ts | 90 ++++++++++++++++++- packages/blue-sdk/src/vault/v2/VaultV2.ts | 43 ++++++--- .../blue-sdk/src/vault/v2/VaultV2Adapter.ts | 10 +++ .../vault/v2/VaultV2MorphoMarketV1Adapter.ts | 13 +++ .../v2/VaultV2MorphoMarketV1AdapterV2.ts | 13 +++ .../vault/v2/VaultV2MorphoVaultV1Adapter.ts | 14 +++ 7 files changed, 178 insertions(+), 13 deletions(-) create mode 100644 .changeset/vault-v2-accrue-nested-entities.md diff --git a/.changeset/vault-v2-accrue-nested-entities.md b/.changeset/vault-v2-accrue-nested-entities.md new file mode 100644 index 000000000..f1b2b9057 --- /dev/null +++ b/.changeset/vault-v2-accrue-nested-entities.md @@ -0,0 +1,8 @@ +--- +"@morpho-org/blue-sdk": minor +"@morpho-org/blue-sdk-viem": patch +"@morpho-org/morpho-sdk": patch +"@morpho-org/wdk-protocol-lending-morpho-evm": patch +--- + +Accrue every underlying adapter, market, and position when accruing a Vault V2, so `AccrualVaultV2.accrueInterest` returns an entity graph whose entire nested state shares one `lastUpdate` — aligning Vault V2 with the existing MetaMorpho V1 (`AccrualVault.accrueInterest`) behavior, rather than leaving nested adapters at pre-accrual state. Adds an additive `accrueInterest(timestamp?)` method to the `IAccrualVaultV2Adapter` interface and each adapter implementation. Vault-level `_totalAssets`, `totalSupply`, and fee shares are byte-for-byte unchanged. diff --git a/packages/blue-sdk/src/vault/v2/VaultV2.test.ts b/packages/blue-sdk/src/vault/v2/VaultV2.test.ts index 4fd135a04..a0942b542 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2.test.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2.test.ts @@ -49,9 +49,10 @@ function adapterBaseInput(): Omit { function accrualAdapter( overrides: Partial = {}, ): IAccrualVaultV2Adapter { - return { + const adapter: IAccrualVaultV2Adapter = { ...vaultV2AdapterInput({ type: "AccrualAdapter" }), realAssets: () => 1_100n, + accrueInterest: () => adapter, maxDeposit: (_data, assets) => ({ value: BigInt(assets), limiter: CapacityLimitReason.balance, @@ -62,6 +63,7 @@ function accrualAdapter( }), ...overrides, }; + return adapter; } function accrualVaultV2( @@ -323,6 +325,32 @@ describe("AccrualVaultV2.accrueInterest", () => { expect(result.managementFeeShares).toBe(0n); expect(result.vault.totalSupply).toBe(1_000n + result.performanceFeeShares); }); + + test("behavior: accrues nested adapters and the liquidity adapter to the same timestamp", () => { + const adapter = new AccrualVaultV2MorphoMarketV1Adapter( + { + ...adapterBaseInput(), + marketParamsList: [new MarketParams(marketParams())], + }, + [accrualPosition({ supplyShares: 100n })], + ); + const vault = accrualVaultV2(adapter); + + const { vault: accrued } = vault.accrueInterest(101n); + + expect(accrued.lastUpdate).toBe(101n); + + const accruedAdapter = accrued + .accrualAdapters[0] as AccrualVaultV2MorphoMarketV1Adapter; + const accruedLiquidity = + accrued.accrualLiquidityAdapter as AccrualVaultV2MorphoMarketV1Adapter; + expect(accruedAdapter).not.toBe(adapter); + expect(accruedAdapter.positions[0]?.market.lastUpdate).toBe(101n); + expect(accruedLiquidity.positions[0]?.market.lastUpdate).toBe(101n); + + // Accrual is non-mutating: the source adapter keeps its original state. + expect(adapter.positions[0]?.market.lastUpdate).toBe(100n); + }); }); describe("VaultV2Adapter", () => { @@ -403,6 +431,21 @@ describe("AccrualVaultV2MorphoMarketV1Adapter", () => { limiter: CapacityLimitReason.position, }); }); + + test("accrueInterest accrues underlying positions and preserves realAssets", () => { + const position = accrualPosition({ supplyShares: 100n }); + const adapter = new AccrualVaultV2MorphoMarketV1Adapter( + { ...adapterBaseInput(), marketParamsList: [position.market.params] }, + [position], + ); + + const accrued = adapter.accrueInterest(101n); + + expect(accrued).not.toBe(adapter); + expect(accrued.positions[0]?.market.lastUpdate).toBe(101n); + expect(adapter.positions[0]?.market.lastUpdate).toBe(100n); + expect(accrued.realAssets(101n)).toBe(adapter.realAssets(101n)); + }); }); describe("VaultV2MorphoMarketV1AdapterV2", () => { @@ -518,6 +561,26 @@ describe("AccrualVaultV2MorphoMarketV1AdapterV2", () => { limiter: CapacityLimitReason.position, }); }); + + test("accrueInterest accrues underlying markets and preserves realAssets", () => { + const m = market(); + const adapter = new AccrualVaultV2MorphoMarketV1AdapterV2( + { + ...adapterBaseInput(), + marketIds: [m.id], + adaptiveCurveIrm: ADAPTER, + supplyShares: { [m.id]: 100n }, + }, + [m], + ); + + const accrued = adapter.accrueInterest(101n); + + expect(accrued).not.toBe(adapter); + expect(accrued.markets[0]?.lastUpdate).toBe(101n); + expect(adapter.markets[0]?.lastUpdate).toBe(100n); + expect(accrued.realAssets(101n)).toBe(adapter.realAssets(101n)); + }); }); describe("VaultV2MorphoVaultV1Adapter", () => { @@ -567,4 +630,29 @@ describe("AccrualVaultV2MorphoVaultV1Adapter", () => { limiter: CapacityLimitReason.liquidity, }); }); + + test("accrueInterest delegates to the underlying V1 vault's accrueInterest", () => { + const accruedVaultV1 = { + toAssets: (shares: bigint) => shares, + } as AccrualVault; + let accruedAt: bigint | undefined; + const accrualVaultV1 = { + accrueInterest: (timestamp?: bigint) => { + accruedAt = timestamp; + return accruedVaultV1; + }, + } as AccrualVault; + const adapter = new AccrualVaultV2MorphoVaultV1Adapter( + { ...adapterBaseInput(), morphoVaultV1: RECIPIENT }, + accrualVaultV1, + 10n, + ); + + const accrued = adapter.accrueInterest(5n); + + expect(accrued).not.toBe(adapter); + expect(accruedAt).toBe(5n); + expect(accrued.accrualVaultV1).toBe(accruedVaultV1); + expect(accrued.shares).toBe(10n); + }); }); diff --git a/packages/blue-sdk/src/vault/v2/VaultV2.ts b/packages/blue-sdk/src/vault/v2/VaultV2.ts index 41ae77f8e..c818d7d2a 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2.ts @@ -233,19 +233,21 @@ export class AccrualVaultV2 extends VaultV2 implements IAccrualVaultV2 { } /** - * Returns a new vault derived from this vault, whose interest has been accrued up to the given timestamp. - * Performance and management fee shares are zero when the corresponding fee recipient cannot receive vault shares. - * @param timestamp The timestamp at which to accrue interest. Must be greater than or equal to the vault's `lastUpdate`. + * Returns a new vault derived from this vault, whose interest — together with + * that of every adapter, market, and position it holds — has been accrued up to + * the given timestamp, so the entire returned entity graph shares one + * `lastUpdate`. + * Performance and management fee shares are zero when the corresponding fee + * recipient cannot receive vault shares. + * @param timestamp The timestamp at which to accrue interest. Must be greater + * than or equal to the vault's `lastUpdate` and to each underlying market's + * `lastUpdate`. + * @throws {VaultV2Errors.InvalidInterestAccrual} when `timestamp` precedes the + * vault's `lastUpdate`. + * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes an + * underlying market's `lastUpdate`. */ public accrueInterest(timestamp: BigIntish) { - const vault = new AccrualVaultV2( - this, - this.accrualLiquidityAdapter, - this.accrualAdapters, - this.assetBalance, - this.forceDeallocatePenalties, - ); - // biome-ignore lint/style/noParameterAssign: TODO refactor to avoid mutating parameter timestamp = BigInt(timestamp); @@ -257,6 +259,23 @@ export class AccrualVaultV2 extends VaultV2 implements IAccrualVaultV2 { this.lastUpdate, ); + // Accrue every nested adapter (and the liquidity adapter) to the same + // timestamp, so the returned vault exposes an entity graph that shares one + // `lastUpdate` rather than pre-accrual market state. + const accrualAdapters = this.accrualAdapters.map((adapter) => + adapter.accrueInterest(timestamp), + ); + const accrualLiquidityAdapter = + this.accrualLiquidityAdapter?.accrueInterest(timestamp); + + const vault = new AccrualVaultV2( + this, + accrualLiquidityAdapter, + accrualAdapters, + this.assetBalance, + this.forceDeallocatePenalties, + ); + // Corresponds to the `firstTotalAssets == 0` onchain check. if (elapsed === 0n) return { vault, performanceFeeShares: 0n, managementFeeShares: 0n }; @@ -300,7 +319,7 @@ export class AccrualVaultV2 extends VaultV2 implements IAccrualVaultV2 { vault._totalAssets = newTotalAssets; if (performanceFeeShares) vault.totalSupply += performanceFeeShares; if (managementFeeShares) vault.totalSupply += managementFeeShares; - vault.lastUpdate = BigInt(timestamp); + vault.lastUpdate = timestamp; return { vault, performanceFeeShares, managementFeeShares }; } diff --git a/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts index e701bb5e8..23f7e5a09 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts @@ -38,6 +38,16 @@ export abstract class VaultV2Adapter implements IVaultV2Adapter { export interface IAccrualVaultV2Adapter extends IVaultV2Adapter { realAssets(timestamp: BigIntish): bigint; + /** + * Returns a new adapter derived from this one, whose underlying market state + * has been accrued up to the given timestamp. Lets a fully-accrued vault expose + * an entity graph in which every adapter, market, and position shares one + * `lastUpdate` instead of pre-accrual state. + * @param timestamp The timestamp at which to accrue interest. Must be greater + * than or equal to each underlying market's `lastUpdate`. + */ + accrueInterest(timestamp?: BigIntish): IAccrualVaultV2Adapter; + /** * Returns the maximum amount of assets that can be deposited to this adapter. * @param assets The maximum amount of assets to deposit. diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts index 93d5cc712..35e68a48f 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts @@ -104,6 +104,19 @@ export class AccrualVaultV2MorphoMarketV1Adapter ); } + /** + * Returns a new adapter whose underlying positions (and their markets) have + * been accrued up to the given timestamp. + * @param timestamp The timestamp at which to accrue interest. Must be greater + * than or equal to each position's market `lastUpdate`. + */ + accrueInterest(timestamp?: BigIntish) { + return new AccrualVaultV2MorphoMarketV1Adapter( + this, + this.positions.map((position) => position.accrueInterest(timestamp)), + ); + } + maxDeposit(_data: Hex, assets: BigIntish) { return { value: BigInt(assets), diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts index fa243d5b1..012f343ca 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts @@ -114,6 +114,19 @@ export class AccrualVaultV2MorphoMarketV1AdapterV2 ); } + /** + * Returns a new adapter whose underlying markets have been accrued up to the + * given timestamp. + * @param timestamp The timestamp at which to accrue interest. Must be greater + * than or equal to each market's `lastUpdate`. + */ + accrueInterest(timestamp?: BigIntish) { + return new AccrualVaultV2MorphoMarketV1AdapterV2( + this, + this.markets.map((market) => market.accrueInterest(timestamp)), + ); + } + maxDeposit(_data: Hex, assets: BigIntish) { return { value: BigInt(assets), diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts index 8510a0a39..f9ed2cc55 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts @@ -74,6 +74,20 @@ export class AccrualVaultV2MorphoVaultV1Adapter return this.accrualVaultV1.accrueInterest(timestamp).toAssets(this.shares); } + /** + * Returns a new adapter whose underlying MetaMorpho V1 vault (and its market + * positions) has been accrued up to the given timestamp. + * @param timestamp The timestamp at which to accrue interest. Must be greater + * than or equal to each underlying market's `lastUpdate`. + */ + accrueInterest(timestamp?: BigIntish) { + return new AccrualVaultV2MorphoVaultV1Adapter( + this, + this.accrualVaultV1.accrueInterest(timestamp), + this.shares, + ); + } + maxDeposit(_data: Hex, assets: BigIntish) { return this.accrualVaultV1.maxDeposit(assets); } From 0fb3f208955da02c0f384d88c9afe1ea0d87d785 Mon Sep 17 00:00:00 2001 From: Foulk plb | Morpho <71005796+Foulks-Plb@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:59:52 +0200 Subject: [PATCH 2/2] fix(blue-sdk): address Codex review on Vault V2 nested accrual Make the new IAccrualVaultV2Adapter.accrueInterest method optional and guard the vault call with a fallback, so adding it stays backward compatible (minor, not major): pre-existing TS/JS adapters without it are left at their pre-accrual state instead of failing to compile or crashing at runtime. Require the timestamp argument so every nested market accrues to the same instant rather than each defaulting to its own lastUpdate. Complete the JSDoc contract (@returns, @throws, @example) on the interface method, its implementations, and AccrualVaultV2.accrueInterest. Co-Authored-By: Claude Opus 4.8 (1M context) --- .changeset/vault-v2-accrue-nested-entities.md | 2 +- .../blue-sdk/src/vault/v2/VaultV2.test.ts | 23 +++++++++++++++ packages/blue-sdk/src/vault/v2/VaultV2.ts | 28 +++++++++++++++---- .../blue-sdk/src/vault/v2/VaultV2Adapter.ts | 26 +++++++++++++++-- .../vault/v2/VaultV2MorphoMarketV1Adapter.ts | 11 +++++++- .../v2/VaultV2MorphoMarketV1AdapterV2.ts | 11 +++++++- .../vault/v2/VaultV2MorphoVaultV1Adapter.ts | 11 +++++++- 7 files changed, 100 insertions(+), 12 deletions(-) diff --git a/.changeset/vault-v2-accrue-nested-entities.md b/.changeset/vault-v2-accrue-nested-entities.md index f1b2b9057..c78fd8ca4 100644 --- a/.changeset/vault-v2-accrue-nested-entities.md +++ b/.changeset/vault-v2-accrue-nested-entities.md @@ -5,4 +5,4 @@ "@morpho-org/wdk-protocol-lending-morpho-evm": patch --- -Accrue every underlying adapter, market, and position when accruing a Vault V2, so `AccrualVaultV2.accrueInterest` returns an entity graph whose entire nested state shares one `lastUpdate` — aligning Vault V2 with the existing MetaMorpho V1 (`AccrualVault.accrueInterest`) behavior, rather than leaving nested adapters at pre-accrual state. Adds an additive `accrueInterest(timestamp?)` method to the `IAccrualVaultV2Adapter` interface and each adapter implementation. Vault-level `_totalAssets`, `totalSupply`, and fee shares are byte-for-byte unchanged. +Accrue every underlying adapter, market, and position when accruing a Vault V2, so `AccrualVaultV2.accrueInterest` returns an entity graph whose entire nested state shares one `lastUpdate` — aligning Vault V2 with the existing MetaMorpho V1 (`AccrualVault.accrueInterest`) behavior, rather than leaving nested adapters at pre-accrual state. Adds a backward-compatible optional `accrueInterest(timestamp)` method to the `IAccrualVaultV2Adapter` interface — adapters that do not implement it are left at their pre-accrual state — and implements it on each built-in adapter. Vault-level `_totalAssets`, `totalSupply`, and fee shares are byte-for-byte unchanged. diff --git a/packages/blue-sdk/src/vault/v2/VaultV2.test.ts b/packages/blue-sdk/src/vault/v2/VaultV2.test.ts index a0942b542..fcce42c96 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2.test.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2.test.ts @@ -351,6 +351,29 @@ describe("AccrualVaultV2.accrueInterest", () => { // Accrual is non-mutating: the source adapter keeps its original state. expect(adapter.positions[0]?.market.lastUpdate).toBe(100n); }); + + test("behavior: leaves adapters without accrueInterest at their pre-accrual state", () => { + // An adapter built before the optional `accrueInterest` method existed. + const legacyAdapter: IAccrualVaultV2Adapter = { + ...vaultV2AdapterInput({ type: "LegacyAdapter" }), + realAssets: () => 1_100n, + maxDeposit: (_data, assets) => ({ + value: BigInt(assets), + limiter: CapacityLimitReason.balance, + }), + maxWithdraw: () => ({ + value: 500n, + limiter: CapacityLimitReason.balance, + }), + }; + const vault = accrualVaultV2(legacyAdapter); + + const { vault: accrued } = vault.accrueInterest(101n); + + expect(accrued.lastUpdate).toBe(101n); + expect(accrued.accrualAdapters[0]).toBe(legacyAdapter); + expect(accrued.accrualLiquidityAdapter).toBe(legacyAdapter); + }); }); describe("VaultV2Adapter", () => { diff --git a/packages/blue-sdk/src/vault/v2/VaultV2.ts b/packages/blue-sdk/src/vault/v2/VaultV2.ts index c818d7d2a..e1c16892d 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2.ts @@ -236,16 +236,32 @@ export class AccrualVaultV2 extends VaultV2 implements IAccrualVaultV2 { * Returns a new vault derived from this vault, whose interest — together with * that of every adapter, market, and position it holds — has been accrued up to * the given timestamp, so the entire returned entity graph shares one - * `lastUpdate`. + * `lastUpdate`. Adapters that do not implement `accrueInterest` are left at + * their pre-accrual state. * Performance and management fee shares are zero when the corresponding fee * recipient cannot receive vault shares. * @param timestamp The timestamp at which to accrue interest. Must be greater * than or equal to the vault's `lastUpdate` and to each underlying market's * `lastUpdate`. + * @returns An object with the accrued `vault` (a new `AccrualVaultV2`) and the + * `performanceFeeShares` and `managementFeeShares` minted by the accrual. * @throws {VaultV2Errors.InvalidInterestAccrual} when `timestamp` precedes the * vault's `lastUpdate`. * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes an * underlying market's `lastUpdate`. + * @example + * ```ts + * import { createPublicClient, http } from "viem"; + * import { mainnet } from "viem/chains"; + * import { fetchAccrualVaultV2 } from "@morpho-org/blue-sdk-viem"; + * + * const client = createPublicClient({ chain: mainnet, transport: http() }); + * const vault = await fetchAccrualVaultV2(vaultAddress, client); + * const { vault: accrued, performanceFeeShares } = vault.accrueInterest( + * vault.lastUpdate, + * ); + * // accrued.toAssets(accrued.totalSupply) reflects assets at vault.lastUpdate + * ``` */ public accrueInterest(timestamp: BigIntish) { // biome-ignore lint/style/noParameterAssign: TODO refactor to avoid mutating parameter @@ -261,12 +277,14 @@ export class AccrualVaultV2 extends VaultV2 implements IAccrualVaultV2 { // Accrue every nested adapter (and the liquidity adapter) to the same // timestamp, so the returned vault exposes an entity graph that shares one - // `lastUpdate` rather than pre-accrual market state. - const accrualAdapters = this.accrualAdapters.map((adapter) => - adapter.accrueInterest(timestamp), + // `lastUpdate` rather than pre-accrual market state. Adapters that predate + // `accrueInterest` are left as-is for backward compatibility. + const accrualAdapters = this.accrualAdapters.map( + (adapter) => adapter.accrueInterest?.(timestamp) ?? adapter, ); const accrualLiquidityAdapter = - this.accrualLiquidityAdapter?.accrueInterest(timestamp); + this.accrualLiquidityAdapter?.accrueInterest?.(timestamp) ?? + this.accrualLiquidityAdapter; const vault = new AccrualVaultV2( this, diff --git a/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts index 23f7e5a09..dcea3a349 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2Adapter.ts @@ -43,10 +43,30 @@ export interface IAccrualVaultV2Adapter extends IVaultV2Adapter { * has been accrued up to the given timestamp. Lets a fully-accrued vault expose * an entity graph in which every adapter, market, and position shares one * `lastUpdate` instead of pre-accrual state. - * @param timestamp The timestamp at which to accrue interest. Must be greater - * than or equal to each underlying market's `lastUpdate`. + * + * Optional for backward compatibility: an adapter that does not implement it is + * left at its pre-accrual state by the vault's `accrueInterest`. + * @param timestamp The timestamp at which to accrue interest. Required so every + * nested market accrues to the same instant. Must be greater than or equal to + * each underlying market's `lastUpdate`. + * @returns A new adapter of the same concrete type, with every underlying + * market accrued to `timestamp`. + * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes an + * underlying market's `lastUpdate`. + * @example + * ```ts + * import { createPublicClient, http } from "viem"; + * import { mainnet } from "viem/chains"; + * import { fetchAccrualVaultV2 } from "@morpho-org/blue-sdk-viem"; + * + * const client = createPublicClient({ chain: mainnet, transport: http() }); + * const vault = await fetchAccrualVaultV2(vaultAddress, client); + * const [adapter] = vault.accrualAdapters; + * const accrued = adapter?.accrueInterest(vault.lastUpdate); + * // accrued.realAssets(vault.lastUpdate) reflects state at the shared timestamp + * ``` */ - accrueInterest(timestamp?: BigIntish): IAccrualVaultV2Adapter; + accrueInterest?(timestamp: BigIntish): IAccrualVaultV2Adapter; /** * Returns the maximum amount of assets that can be deposited to this adapter. diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts index 35e68a48f..062eaf7e9 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1Adapter.ts @@ -109,8 +109,17 @@ export class AccrualVaultV2MorphoMarketV1Adapter * been accrued up to the given timestamp. * @param timestamp The timestamp at which to accrue interest. Must be greater * than or equal to each position's market `lastUpdate`. + * @returns A new `AccrualVaultV2MorphoMarketV1Adapter` with every position + * accrued to `timestamp`. + * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes a + * position's market `lastUpdate`. + * @example + * ```ts + * const accrued = adapter.accrueInterest(adapter.positions[0]!.market.lastUpdate); + * // accrued.positions[0]!.market.lastUpdate === the passed timestamp + * ``` */ - accrueInterest(timestamp?: BigIntish) { + accrueInterest(timestamp: BigIntish) { return new AccrualVaultV2MorphoMarketV1Adapter( this, this.positions.map((position) => position.accrueInterest(timestamp)), diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts index 012f343ca..a87bf1747 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoMarketV1AdapterV2.ts @@ -119,8 +119,17 @@ export class AccrualVaultV2MorphoMarketV1AdapterV2 * given timestamp. * @param timestamp The timestamp at which to accrue interest. Must be greater * than or equal to each market's `lastUpdate`. + * @returns A new `AccrualVaultV2MorphoMarketV1AdapterV2` with every market + * accrued to `timestamp`. + * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes a + * market's `lastUpdate`. + * @example + * ```ts + * const accrued = adapter.accrueInterest(adapter.markets[0]!.lastUpdate); + * // accrued.markets[0]!.lastUpdate === the passed timestamp + * ``` */ - accrueInterest(timestamp?: BigIntish) { + accrueInterest(timestamp: BigIntish) { return new AccrualVaultV2MorphoMarketV1AdapterV2( this, this.markets.map((market) => market.accrueInterest(timestamp)), diff --git a/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts b/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts index f9ed2cc55..2353bf9c2 100644 --- a/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts +++ b/packages/blue-sdk/src/vault/v2/VaultV2MorphoVaultV1Adapter.ts @@ -79,8 +79,17 @@ export class AccrualVaultV2MorphoVaultV1Adapter * positions) has been accrued up to the given timestamp. * @param timestamp The timestamp at which to accrue interest. Must be greater * than or equal to each underlying market's `lastUpdate`. + * @returns A new `AccrualVaultV2MorphoVaultV1Adapter` wrapping the V1 vault + * accrued to `timestamp`. + * @throws {BlueErrors.InvalidInterestAccrual} when `timestamp` precedes an + * underlying market's `lastUpdate`. + * @example + * ```ts + * const accrued = adapter.accrueInterest(timestamp); + * // accrued.realAssets(timestamp) reflects the V1 vault's assets at `timestamp` + * ``` */ - accrueInterest(timestamp?: BigIntish) { + accrueInterest(timestamp: BigIntish) { return new AccrualVaultV2MorphoVaultV1Adapter( this, this.accrualVaultV1.accrueInterest(timestamp),