diff --git a/packages/blue-sdk-ethers/test/e2e/Market.test.ts b/packages/blue-sdk-ethers/test/e2e/Market.test.ts index 2848119c..019e9a2c 100644 --- a/packages/blue-sdk-ethers/test/e2e/Market.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Market.test.ts @@ -3,7 +3,7 @@ import { Wallet, toBigInt } from "ethers"; import { MorphoBlue__factory } from "ethers-types"; import { ethers } from "hardhat"; -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import type { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; import { setCode, time } from "@nomicfoundation/hardhat-network-helpers"; import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; @@ -61,7 +61,7 @@ describe("augment/Market", () => { it("should fetch price and rate if idle market", async () => { const expectedData = { - config: MAINNET_MARKETS.idle_usdc, + config: MAINNET_MARKETS.usdc_idle, totalSupplyAssets: 0n, totalSupplyShares: 0n, totalBorrowAssets: 0n, @@ -72,7 +72,7 @@ describe("augment/Market", () => { rateAtTarget: undefined, }; - const value = await Market.fetch(MAINNET_MARKETS.idle_usdc.id, signer); + const value = await Market.fetch(MAINNET_MARKETS.usdc_idle.id, signer); expect(value).to.eql(expectedData); }); diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts index d152abe1..1f5c8219 100644 --- a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts @@ -73,10 +73,19 @@ export const handleMetaMorphoDepositOperation: OperationHandler< let toSupply = assets; for (const id of vault.supplyQueue) { - const { supplyAssets } = data - .getAccrualPosition(address, id) - .accrueInterest(data.block.timestamp); const { cap } = data.getVaultMarketConfig(address, id); + if (cap === 0n) continue; + + handleBlueOperation( + { + type: "Blue_AccrueInterest", + sender: address, + args: { id }, + }, + data, + ); + + const { supplyAssets } = data.getAccrualPosition(address, id); const suppliable = MathLib.zeroFloorSub(cap, supplyAssets); if (suppliable === 0n) continue; @@ -86,7 +95,7 @@ export const handleMetaMorphoDepositOperation: OperationHandler< handleBlueOperation( { type: "Blue_Supply", - sender: zeroAddress, // Bypass the vault balance check. + sender: address, args: { id, assets: toSupplyInMarket, diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts index f5348230..e37786fc 100644 --- a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts @@ -119,7 +119,7 @@ export const handleMetaMorphoWithdrawOperation: OperationHandler< address: vault.config.asset, args: { amount: assets, - from: zeroAddress, // Bypass the vault balance check. + from: address, to: receiver, }, }, diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts index d3d19e1e..e641c980 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts @@ -1,46 +1,84 @@ -import _omit from "lodash/omit"; - import { ChainId, addresses } from "@morpho-org/blue-sdk"; +import { blueAbi } from "@morpho-org/blue-sdk-viem"; import { markets } from "@morpho-org/morpho-test"; import { getLast } from "@morpho-org/morpho-ts"; +import { renderHook, waitFor } from "@morpho-org/test"; import { describe, expect } from "vitest"; -import { type Operation, simulateOperations } from "../../../../src"; -import { test } from "../../setup"; +import { + type MinimalBlock, + simulateOperations, + useSimulationState, +} from "../../../../src/index.js"; +import { test } from "../../setup.js"; const { morpho } = addresses[ChainId.EthMainnet]; const { usdc_wstEth } = markets[ChainId.EthMainnet]; describe("Blue_AccrueInterest", () => { - test("should accrue interest accurately", async ({ client }) => { - const operations: Operation[] = [ - { - type: "Blue_AccrueInterest", - sender: client.account.address, - args: { - id: usdc_wstEth.id, - }, - }, - ]; + test("should accrue interest accurately", async ({ + wagmi: { config, client }, + }) => { + const block = await client.getBlock(); - const { value: dataBefore } = await simulationService.data; + const { result, rerender } = await renderHook( + config, + (block: MinimalBlock) => + useSimulationState({ + marketIds: [usdc_wstEth.id], + users: [], + tokens: [], + vaults: [], + block, + accrueInterest: false, + }), + { initialProps: block }, + ); - const steps = simulateOperations(operations, dataBefore); + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - expect(steps.length).to.equal(2); + const dataBefore = result.current.data!; - await client.setNextBlockTimestamp(dataBefore.timestamp); + dataBefore.block.number += 1n; + dataBefore.block.timestamp += 1n; - await MorphoBlue__factory.connect(morpho, signer).accrueInterest( - usdc_wstEth, + const steps = simulateOperations( + [ + { + type: "Blue_AccrueInterest", + sender: client.account.address, + args: { + id: usdc_wstEth.id, + }, + }, + ], + dataBefore, ); - await mine(0); - const expected = getLast(steps); - const { value: data } = await simulationService.data; + expect(steps.length).toBe(2); + + await client.setNextBlockTimestamp({ + timestamp: dataBefore.block.timestamp, + }); + + await client.writeContractWait({ + address: morpho, + abi: blueAbi, + functionName: "accrueInterest", + args: [ + { + collateralToken: usdc_wstEth.collateralToken, + loanToken: usdc_wstEth.loanToken, + oracle: usdc_wstEth.oracle, + irm: usdc_wstEth.irm, + lltv: usdc_wstEth.lltv, + }, + ], + }); - expected.blockNumber += 1n; + await rerender(await client.getBlock()); + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + expect(result.current.data).toStrictEqual(getLast(steps)); }); }); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts index 2902a09d..67f30446 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts @@ -1,88 +1,99 @@ -import { expect } from "chai"; -import { parseUnits } from "ethers"; -import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; -import { ethers } from "hardhat"; -import { deal } from "hardhat-deal"; -import _omit from "lodash/omit"; - -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; - -import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; -import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; -import { mine, setUp } from "@morpho-org/morpho-test"; - -import { SimulationService, simulateOperations } from "../../../../src"; -import { steakUsdc } from "../../fixtures"; +import { renderHook, waitFor } from "@morpho-org/test"; +import { describe, expect } from "vitest"; + +import { ChainId } from "@morpho-org/blue-sdk"; +import { metaMorphoAbi } from "@morpho-org/blue-sdk-viem"; +import { markets, vaults } from "@morpho-org/morpho-test"; +import { getLast } from "@morpho-org/morpho-ts"; +import { erc20Abi, parseUnits } from "viem"; +import { + type MinimalBlock, + simulateOperations, + useSimulationState, +} from "../../../../src/index.js"; +import { test } from "../../setup.js"; + +const { usdc_wstEth, usdc_idle, usdc_wbtc, usdc_wbIB01 } = + markets[ChainId.EthMainnet]; +const { steakUsdc } = vaults[ChainId.EthMainnet]; describe("MetaMorpho_AccrueInterest", () => { - let signer: SignerWithAddress; - - let simulationService: SimulationService; - - setUp(async () => { - signer = (await ethers.getSigners())[0]!; - }); - - afterEach(async () => { - simulationService?.chainService.close(); - simulationService?.metaMorphoService.blueService.close(); - simulationService?.metaMorphoService.close(); - simulationService?.close(); - }); - - test("should accrue interest accurately upon deposit", async () => { + test("should accrue interest accurately upon deposit", async ({ + wagmi: { config, client }, + }) => { const assets = parseUnits("100", 6); - await deal(steakUsdc.asset, signer.address, assets); - - await ( - await ERC20__factory.connect(steakUsdc.asset, signer).approve( - steakUsdc.address, - assets, - ) - ).wait(); - - simulationService = new SimulationService( - new MetaMorphoService( - new BlueService(new ChainService(signer), { - users: [signer.address], + await client.deal({ + erc20: steakUsdc.asset, + recipient: client.account.address, + amount: assets, + }); + + await client.writeContract({ + address: steakUsdc.asset, + abi: erc20Abi, + functionName: "approve", + args: [steakUsdc.address, assets], + }); + + const block = await client.getBlock(); + + const { result, rerender } = await renderHook( + config, + (block: MinimalBlock) => + useSimulationState({ + marketIds: [ + usdc_wstEth.id, + usdc_idle.id, + usdc_wbtc.id, + usdc_wbIB01.id, + ], + users: [client.account.address, steakUsdc.address], + tokens: [steakUsdc.asset], + vaults: [steakUsdc.address], + block, + accrueInterest: false, }), - { vaults: [steakUsdc.address] }, - ), + { initialProps: block }, ); - const { value: dataBefore } = await simulationService.data; + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); + + const dataBefore = result.current.data!; + + dataBefore.block.number += 1n; + dataBefore.block.timestamp += 1n; const steps = simulateOperations( [ { type: "MetaMorpho_Deposit", - sender: signer.address, + sender: client.account.address, address: steakUsdc.address, args: { assets, - owner: signer.address, + owner: client.account.address, }, }, ], dataBefore, ); - expect(steps.length).to.equal(2); - - await setNextBlockTimestamp(dataBefore.timestamp); - await MetaMorpho__factory.connect(steakUsdc.address, signer).deposit( - assets, - signer.address, - ); - await mine(0); + expect(steps.length).toBe(2); - const { value: data } = await simulationService.data; + await client.setNextBlockTimestamp({ + timestamp: dataBefore.block.timestamp, + }); + await client.writeContractWait({ + address: steakUsdc.address, + abi: metaMorphoAbi, + functionName: "deposit", + args: [assets, client.account.address], + }); - const expected = getLast(steps); - expected.blockNumber += 1n; + await rerender(await client.getBlock()); + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + expect(result.current.data).toStrictEqual(getLast(steps)); }); }); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts index e53432f8..a32527d6 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts @@ -4,7 +4,7 @@ import { MetaMorpho__factory, PublicAllocator__factory } from "ethers-types"; import { ethers } from "hardhat"; import _omit from "lodash/omit"; -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import type { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; @@ -55,7 +55,7 @@ describe("MetaMorpho_PublicReallocate", () => { }, }, { - id: MAINNET_MARKETS.idle_usdc.id, + id: MAINNET_MARKETS.usdc_idle.id, caps: { maxIn: assets, maxOut: 0n, @@ -82,7 +82,7 @@ describe("MetaMorpho_PublicReallocate", () => { address: steakUsdc.address, args: { withdrawals: [{ id: MAINNET_MARKETS.usdc_wstEth.id, assets }], - supplyMarketId: MAINNET_MARKETS.idle_usdc.id, + supplyMarketId: MAINNET_MARKETS.usdc_idle.id, }, }, ], @@ -97,7 +97,7 @@ describe("MetaMorpho_PublicReallocate", () => { .reallocateTo( steakUsdc.address, [{ marketParams: MAINNET_MARKETS.usdc_wstEth, amount: assets }], - MAINNET_MARKETS.idle_usdc, + MAINNET_MARKETS.usdc_idle, { value: fee }, ); await mine(0); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts index 36b26c26..c0ba151a 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts @@ -4,7 +4,7 @@ import { MetaMorpho__factory } from "ethers-types"; import { ethers } from "hardhat"; import _omit from "lodash/omit"; -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import type { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; @@ -64,7 +64,7 @@ describe("MetaMorpho_Reallocate", () => { id: MAINNET_MARKETS.usdc_wstEth.id, assets, }, - { id: MAINNET_MARKETS.idle_usdc.id, assets: MaxUint256 }, + { id: MAINNET_MARKETS.usdc_idle.id, assets: MaxUint256 }, ], }, ], @@ -79,7 +79,7 @@ describe("MetaMorpho_Reallocate", () => { marketParams: MAINNET_MARKETS.usdc_wstEth, assets, }, - { marketParams: MAINNET_MARKETS.idle_usdc, assets: MaxUint256 }, + { marketParams: MAINNET_MARKETS.usdc_idle, assets: MaxUint256 }, ]); await mine(0); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts index 2f4c8621..5d798b30 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts @@ -1,93 +1,107 @@ -import { expect } from "chai"; -import { parseUnits } from "ethers"; -import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; -import { ethers } from "hardhat"; -import { deal } from "hardhat-deal"; -import _omit from "lodash/omit"; - -import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; - -import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; -import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; -import { mine, setUp } from "@morpho-org/morpho-test"; - -import { SimulationService, simulateOperations } from "../../../../src"; -import { steakUsdc } from "../../fixtures"; +import { renderHook, waitFor } from "@morpho-org/test"; +import { describe, expect } from "vitest"; + +import { ChainId } from "@morpho-org/blue-sdk"; +import { metaMorphoAbi } from "@morpho-org/blue-sdk-viem"; +import { markets, vaults } from "@morpho-org/morpho-test"; +import { getLast } from "@morpho-org/morpho-ts"; +import { erc20Abi, parseUnits } from "viem"; +import { + type MinimalBlock, + simulateOperations, + useSimulationState, +} from "../../../../src/index.js"; +import { test } from "../../setup.js"; + +const { usdc_wstEth, usdc_idle, usdc_wbtc, usdc_wbIB01 } = + markets[ChainId.EthMainnet]; +const { steakUsdc } = vaults[ChainId.EthMainnet]; describe("MetaMorpho_AccrueInterest", () => { - let signer: SignerWithAddress; - - let simulationService: SimulationService; - - setUp(async () => { - signer = (await ethers.getSigners())[0]!; - }); - - afterEach(async () => { - simulationService?.chainService.close(); - simulationService?.metaMorphoService.blueService.close(); - simulationService?.metaMorphoService.close(); - simulationService?.close(); - }); - - test("should accrue interest accurately upon withdraw", async () => { + test("should accrue interest accurately upon withdraw", async ({ + wagmi: { config, client }, + }) => { const assets = parseUnits("100", 6); - await deal(steakUsdc.asset, signer.address, assets * 2n); - await ERC20__factory.connect(steakUsdc.asset, signer).approve( - steakUsdc.address, - assets * 2n, - ); - await ( - await MetaMorpho__factory.connect(steakUsdc.address, signer).deposit( - assets * 2n, - signer.address, - ) - ).wait(); - - simulationService = new SimulationService( - new MetaMorphoService( - new BlueService(new ChainService(signer), { - users: [signer.address], + await client.deal({ + erc20: steakUsdc.asset, + recipient: client.account.address, + amount: assets * 2n, + }); + + await client.writeContract({ + address: steakUsdc.asset, + abi: erc20Abi, + functionName: "approve", + args: [steakUsdc.address, assets * 2n], + }); + + await client.writeContractWait({ + address: steakUsdc.address, + abi: metaMorphoAbi, + functionName: "deposit", + args: [assets * 2n, client.account.address], + }); + + const block = await client.getBlock(); + + const { result, rerender } = await renderHook( + config, + (block: MinimalBlock) => + useSimulationState({ + marketIds: [ + usdc_wstEth.id, + usdc_idle.id, + usdc_wbtc.id, + usdc_wbIB01.id, + ], + users: [client.account.address, steakUsdc.address], + tokens: [steakUsdc.asset, steakUsdc.address], + vaults: [steakUsdc.address], + block, + accrueInterest: false, }), - { vaults: [steakUsdc.address] }, - ), + { initialProps: block }, ); - const { value: dataBefore } = await simulationService.data; + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); + + const dataBefore = result.current.data!; + + dataBefore.block.number += 1n; + dataBefore.block.timestamp += 1n; const steps = simulateOperations( [ { type: "MetaMorpho_Withdraw", - sender: signer.address, + sender: client.account.address, address: steakUsdc.address, args: { assets, - owner: signer.address, - receiver: signer.address, + owner: client.account.address, + receiver: client.account.address, }, }, ], dataBefore, ); - expect(steps.length).to.equal(2); - - await setNextBlockTimestamp(dataBefore.timestamp); - await MetaMorpho__factory.connect(steakUsdc.address, signer).withdraw( - assets, - signer.address, - signer.address, - ); - await mine(0); + expect(steps.length).toBe(2); - const { value: data } = await simulationService.data; + await client.setNextBlockTimestamp({ + timestamp: dataBefore.block.timestamp, + }); + await client.writeContractWait({ + address: steakUsdc.address, + abi: metaMorphoAbi, + functionName: "withdraw", + args: [assets, client.account.address, client.account.address], + }); - const expected = getLast(steps); - expected.blockNumber += 1n; + await rerender(await client.getBlock()); + await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + expect(result.current.data).toStrictEqual(getLast(steps)); }); }); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/hooks/useSimulationState.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/hooks/useSimulationState.test.ts index 04b5483d..a6315c8b 100644 --- a/packages/blue-sdk-viem-simulation/test/e2e/hooks/useSimulationState.test.ts +++ b/packages/blue-sdk-viem-simulation/test/e2e/hooks/useSimulationState.test.ts @@ -7,7 +7,6 @@ import { describe, expect } from "vitest"; import { Erc20Errors, type MinimalBlock, - type Operation, SimulationState, simulateOperations, useSimulationState, @@ -15,7 +14,8 @@ import { import { test } from "../setup.js"; const { morpho, bundler, permit2, usdc } = addresses[ChainId.EthMainnet]; -const { usdc_wstEth } = markets[ChainId.EthMainnet]; +const { usdc_wbIB01, usdc_wstEth, usdc_wbtc, usdc_idle } = + markets[ChainId.EthMainnet]; const { steakUsdc } = vaults[ChainId.EthMainnet]; describe("useSimulationState", () => { @@ -164,20 +164,23 @@ describe("useSimulationState", () => { await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - const operations: Operation[] = [ - { - type: "Erc20_Transfer", - sender: morpho, - address: usdc, - args: { - amount: 1_000000n, - from: client.account.address, - to: morpho, - }, - }, - ]; - - expect(() => simulateOperations(operations, result.current.data!)).toThrow( + expect(() => + simulateOperations( + [ + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount: 1_000000n, + from: client.account.address, + to: morpho, + }, + }, + ], + result.current.data!, + ), + ).toThrow( new Erc20Errors.InsufficientBalance(usdc, client.account.address).message, ); }); @@ -207,20 +210,23 @@ describe("useSimulationState", () => { await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - const operations: Operation[] = [ - { - type: "Erc20_Transfer", - sender: morpho, - address: usdc, - args: { - amount, - from: client.account.address, - to: morpho, - }, - }, - ]; - - expect(() => simulateOperations(operations, result.current.data!)).toThrow( + expect(() => + simulateOperations( + [ + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount, + from: client.account.address, + to: morpho, + }, + }, + ], + result.current.data!, + ), + ).toThrow( new Erc20Errors.InsufficientAllowance( usdc, client.account.address, @@ -255,31 +261,32 @@ describe("useSimulationState", () => { await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - const operations: Operation[] = [ - { - type: "Erc20_Approve", - sender: client.account.address, - address: usdc, - args: { - spender: morpho, - amount, - }, - }, - { - type: "Erc20_Transfer", - sender: morpho, - address: usdc, - args: { - amount, - from: client.account.address, - to: morpho, - }, - }, - ]; - const data0 = result.current.data!; - const steps = simulateOperations(operations, data0); + const steps = simulateOperations( + [ + { + type: "Erc20_Approve", + sender: client.account.address, + address: usdc, + args: { + spender: morpho, + amount, + }, + }, + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount, + from: client.account.address, + to: morpho, + }, + }, + ], + data0, + ); expect(steps.length).toBe(3); @@ -288,7 +295,7 @@ describe("useSimulationState", () => { await client.setNextBlockTimestamp({ timestamp: data0.block.timestamp + 1n, }); - await client.writeContract({ + await client.writeContractWait({ address: usdc, abi: erc20Abi, functionName: "approve", @@ -311,7 +318,7 @@ describe("useSimulationState", () => { timestamp: data1.block.timestamp + 1n, }); await client.setBalance({ address: morpho, value: BigInt(1e18) }); - await client.writeContract({ + await client.writeContractWait({ account: morpho, address: usdc, abi: erc20Abi, @@ -347,9 +354,9 @@ describe("useSimulationState", () => { const { result } = await renderHook(config, () => useSimulationState({ - marketIds: [usdc_wstEth.id], - users: [client.account.address], - tokens: [usdc], + marketIds: [usdc_wstEth.id, usdc_idle.id, usdc_wbtc.id, usdc_wbIB01.id], + users: [client.account.address, steakUsdc.address, bundler], + tokens: [steakUsdc.asset, steakUsdc.address], vaults: [steakUsdc.address], block, }), @@ -357,122 +364,123 @@ describe("useSimulationState", () => { await waitFor(() => expect(result.current.isFetchingAny).toBeFalsy()); - const operations: Operation[] = [ - { - type: "Erc20_Approve", - sender: client.account.address, - address: steakUsdc.asset, - args: { - spender: permit2, - amount, + const data0 = result.current.data!; + + const steps = simulateOperations( + [ + { + type: "Erc20_Approve", + sender: client.account.address, + address: steakUsdc.asset, + args: { + spender: permit2, + amount, + }, }, - }, - { - type: "Erc20_Permit2", - sender: client.account.address, - address: steakUsdc.asset, - args: { - amount, - spender: bundler, - expiration: MathLib.MAX_UINT_48, - nonce: 0n, + { + type: "Erc20_Permit2", + sender: client.account.address, + address: steakUsdc.asset, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, }, - }, - { - type: "Erc20_Transfer2", - sender: bundler, - address: steakUsdc.asset, - args: { - amount, - from: client.account.address, - to: bundler, + { + type: "Erc20_Transfer2", + sender: bundler, + address: steakUsdc.asset, + args: { + amount, + from: client.account.address, + to: bundler, + }, }, - }, - { - type: "MetaMorpho_Deposit", - sender: bundler, - address: steakUsdc.address, - args: { - assets: amount, - owner: client.account.address, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: amount, + owner: client.account.address, + }, }, - }, - ]; - - const data0 = result.current.data!; - - const steps = simulateOperations(operations, data0); + ], + data0, + ); - expect(steps.length).to.equal(5); + expect(steps.length).toBe(5); expect( steps[0].getHolding(client.account.address, steakUsdc.asset).balance, - ).to.equal(amount); + ).toBe(amount); expect( steps[0].getVaultUser(steakUsdc.address, client.account.address) .allowance, - ).to.equal(0); + ).toBe(0n); expect( steps[0].getHolding(client.account.address, steakUsdc.asset) .permit2Allowances.bundler.amount, - ).to.equal(0); + ).toBe(0n); expect( steps[0].getHolding(client.account.address, steakUsdc.address).balance, - ).to.equal(0); + ).toBe(0n); expect( steps[0].getPosition(steakUsdc.address, usdc_wstEth.id).supplyShares, - ).to.equal(29_378_343_227455118737n); + ).toBe(29_378_343_227455118737n); const step1 = steps[1]!; expect( step1.getHolding(client.account.address, steakUsdc.asset).balance, - ).to.equal(amount); + ).toBe(amount); expect( step1.getHolding(client.account.address, steakUsdc.asset).erc20Allowances .permit2, - ).to.equal(amount); + ).toBe(amount); expect( step1.getHolding(client.account.address, steakUsdc.asset) .permit2Allowances.bundler.amount, - ).to.equal(0); + ).toBe(0n); expect( step1.getHolding(client.account.address, steakUsdc.address).balance, - ).to.equal(0); + ).toBe(0n); expect( step1.getPosition(steakUsdc.address, usdc_wstEth.id).supplyShares, - ).to.equal(29_378_343_227455118737n); + ).toBe(29_378_343_227455118737n); const step2 = steps[2]!; expect( step2.getHolding(client.account.address, steakUsdc.asset).balance, - ).to.equal(amount); + ).toBe(amount); expect( step2.getHolding(client.account.address, steakUsdc.asset).erc20Allowances .permit2, - ).to.equal(amount); + ).toBe(amount); expect( step2.getHolding(client.account.address, steakUsdc.asset) .permit2Allowances.bundler.amount, - ).to.equal(amount); + ).toBe(amount); expect( step2.getHolding(client.account.address, steakUsdc.address).balance, - ).to.equal(0); + ).toBe(0n); expect( step2.getPosition(steakUsdc.address, usdc_wstEth.id).supplyShares, - ).to.equal(29_378_343_227455118737n); + ).toBe(29_378_343_227455118737n); const step4 = steps[4]!; expect( step4.getHolding(client.account.address, steakUsdc.asset).balance, - ).to.equal(0); + ).toBe(0n); expect( step4.getVaultUser(steakUsdc.address, client.account.address).allowance, - ).to.equal(0); + ).toBe(0n); expect( step4.getHolding(client.account.address, steakUsdc.address).balance, - ).to.equal(980_675_703_540782945699252n); + ).toBe(980_675_703_540782945699252n); expect( step4.getPosition(steakUsdc.address, usdc_wstEth.id).supplyShares, - ).to.equal(30_357_464_135047367671n); + ).toBe(30_357_464_135047367671n); }); }); diff --git a/packages/blue-sdk-viem/test/Market.test.ts b/packages/blue-sdk-viem/test/Market.test.ts index 4ec9a872..024c402d 100644 --- a/packages/blue-sdk-viem/test/Market.test.ts +++ b/packages/blue-sdk-viem/test/Market.test.ts @@ -8,7 +8,7 @@ import { blueAbi } from "../src/index.js"; import { test } from "./setup.js"; const { morpho, adaptiveCurveIrm } = addresses[ChainId.EthMainnet]; -const { usdc_wstEth, idle_usdc, eth_wstEth } = markets[ChainId.EthMainnet]; +const { usdc_wstEth, usdc_idle, eth_wstEth } = markets[ChainId.EthMainnet]; describe("augment/Market", () => { test("should fetch market data", async ({ client }) => { @@ -31,7 +31,7 @@ describe("augment/Market", () => { test("should fetch price and rate if idle market", async ({ client }) => { const expectedData = new Market({ - config: idle_usdc, + config: usdc_idle, totalSupplyAssets: 0n, totalSupplyShares: 0n, totalBorrowAssets: 0n, @@ -42,7 +42,7 @@ describe("augment/Market", () => { rateAtTarget: undefined, }); - const value = await Market.fetch(idle_usdc.id, client); + const value = await Market.fetch(usdc_idle.id, client); expect(value).toStrictEqual(expectedData); }); diff --git a/packages/morpho-test/src/fixtures/markets.ts b/packages/morpho-test/src/fixtures/markets.ts index c1838170..1954d1ec 100644 --- a/packages/morpho-test/src/fixtures/markets.ts +++ b/packages/morpho-test/src/fixtures/markets.ts @@ -7,29 +7,32 @@ import { import { randomAddress } from "@morpho-org/test"; import { parseEther, parseUnits, zeroAddress } from "viem"; +const { adaptiveCurveIrm, wNative, usdc, wstEth, wbIB01 } = + addresses[ChainId.EthMainnet]; + export const markets = { [ChainId.EthMainnet]: { eth_wstEth: new MarketConfig({ - loanToken: addresses[ChainId.EthMainnet].wNative, - collateralToken: addresses[ChainId.EthMainnet].wstEth, + loanToken: wNative, + collateralToken: wstEth, oracle: "0x2a01EB9496094dA03c4E364Def50f5aD1280AD72", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("94.5", 16), }), eth_wstEth_2: new MarketConfig({ - loanToken: addresses[ChainId.EthMainnet].wNative, - collateralToken: addresses[ChainId.EthMainnet].wstEth, + loanToken: wNative, + collateralToken: wstEth, oracle: "0xbD60A6770b27E084E8617335ddE769241B0e71D8", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("94.5", 16), }), eth_rEth: new MarketConfig({ - loanToken: addresses[ChainId.EthMainnet].wNative, + loanToken: wNative, collateralToken: "0xae78736Cd615f374D3085123A210448E74Fc6393", oracle: "0x1b4A3F92e5Fffd1d35A98751c9FE4472483579bB", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("94.5", 16), }), @@ -37,47 +40,56 @@ export const markets = { loanToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", collateralToken: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", oracle: "0x008bF4B1cDA0cc9f0e882E0697f036667652E1ef", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), usdt_wstEth: new MarketConfig({ loanToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", - collateralToken: addresses[ChainId.EthMainnet]?.wstEth, + collateralToken: wstEth, oracle: "0x95DB30fAb9A3754e42423000DF27732CB2396992", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), usdc_wbtc: new MarketConfig({ // USDC(wBTC, 86%, Chainlink, AdaptiveCurve) - loanToken: addresses[ChainId.EthMainnet].usdc, + loanToken: usdc, collateralToken: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", oracle: "0xDddd770BADd886dF3864029e4B377B5F6a2B6b83", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), usdc_wstEth: new MarketConfig({ // USDC(wstETH, 86%, Chainlink, AdaptiveCurve) - loanToken: addresses[ChainId.EthMainnet].usdc, - collateralToken: addresses[ChainId.EthMainnet]?.wstEth, + loanToken: usdc, + collateralToken: wstEth, oracle: "0x48F7E36EB6B826B2dF4B2E630B62Cd25e89E40e2", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), usdc_sDai: new MarketConfig({ // USDC(wstETH, 86%, Chainlink, AdaptiveCurve) - loanToken: addresses[ChainId.EthMainnet].usdc, + loanToken: usdc, collateralToken: "0x83F20F44975D03b1b09e64809B757c47f942BEeA", oracle: "0x6CAFE228eC0B0bC2D076577d56D35Fe704318f6d", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, + lltv: parseUnits("96.5", 16), + }), + + usdc_wbIB01: new MarketConfig({ + // USDC(wbIB01, 96.5%, Chainlink, AdaptiveCurve) + loanToken: usdc, + collateralToken: wbIB01, + oracle: "0x6E8F5b2DF218443E87fe8aA9811E6956716dde88", + irm: adaptiveCurveIrm, lltv: parseUnits("96.5", 16), }), - idle_usdc: new MarketConfig({ - loanToken: addresses[ChainId.EthMainnet].usdc, + usdc_idle: new MarketConfig({ + loanToken: usdc, collateralToken: zeroAddress, oracle: zeroAddress, irm: zeroAddress, @@ -88,7 +100,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0xb0Ce26C88e4e7DCa51968b6047f44646f5064278", oracle: "0x077Af6c2D4A75D4145d141F9e9421864C3940CB3", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), @@ -96,7 +108,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0x0ea1a65A2c255f24Ee8D81eA6AaC54Decd9d269e", oracle: "0xd2F7C3B2fC97cC7b6AfDd76D163394680EFc35b9", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), @@ -104,7 +116,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0x3ce8Ec9f3d89aD0A2DdbCC3FDB8991BD241Fc82E", oracle: "0xa9f7900476F43C45Ebf56cEa669B9c960C176112", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), @@ -112,7 +124,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0x6BA072F0d22806F2C52e9792AF47f2D59103BEBE", oracle: "0x18B0d7311a97c5377445C80c768ab5201Bb27B5a", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), @@ -120,7 +132,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0xAc904BAfBb5FB04Deb2b6198FdCEedE75a78Ce5a", oracle: "0xad7e157815df05029125B568E39d5402550d60bb", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), @@ -128,7 +140,7 @@ export const markets = { loanToken: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", collateralToken: "0x385E12cf4040543Bc8C18e05C1298Be5B04f3f5e", oracle: "0x20c4fA59f032bEC6de1905B7201CB88DFD968abA", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: 860000000000000000n, }), @@ -136,7 +148,7 @@ export const markets = { loanToken: "0x0000206329b97DB379d5E1Bf586BbDB969C63274", collateralToken: "0x78Fc2c2eD1A4cDb5402365934aE5648aDAd094d0", oracle: "0x76052A2A28fDCB8124f4686C63C68355b142de3B", - irm: addresses[ChainId.EthMainnet].adaptiveCurveIrm, + irm: adaptiveCurveIrm, lltv: parseUnits("86", 16), }), }, diff --git a/packages/test/src/anvil.ts b/packages/test/src/anvil.ts index 02e009c3..b70bba96 100644 --- a/packages/test/src/anvil.ts +++ b/packages/test/src/anvil.ts @@ -1,14 +1,19 @@ import { spawn } from "node:child_process"; import { http, + type Abi, type Chain, type Client, + type ContractFunctionArgs, + type ContractFunctionName, type HDAccount, type HttpTransport, type PublicActions, type TestActions, type TestRpcSchema, + type WaitForTransactionReceiptReturnType, type WalletActions, + type WriteContractParameters, createTestClient, publicActions, walletActions, @@ -324,15 +329,22 @@ export const spawnAnvil = async ( const stop = await new Promise<() => boolean>((resolve, reject) => { const subprocess = spawn("anvil", toArgs({ ...args, port })); - // subprocess.stdout.on("data", console.debug); - subprocess.stderr.on("stderr", console.warn); - subprocess.stdout.on("data", (data) => { const message = data.toString(); + + // console.debug(message); + if (message.includes("Listening on")) resolve(() => subprocess.kill("SIGINT")); }); - subprocess.stderr.on("data", reject); + + subprocess.stderr.on("data", (data) => { + const message = data.toString(); + + console.warn(message); + + reject(message); + }); }); return { @@ -356,6 +368,26 @@ export const createAnvilTestClient = < PublicActions & WalletActions & { timestamp(): Promise; + writeContractWait< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName< + abi, + "payable" | "nonpayable" + >, + args extends ContractFunctionArgs< + abi, + "payable" | "nonpayable", + functionName + >, + >( + args: WriteContractParameters< + abi, + functionName, + args, + chain, + HDAccount + >, + ): Promise>; } > => createTestClient({ @@ -369,11 +401,32 @@ export const createAnvilTestClient = < .extend(walletActions) .extend((client) => ({ async timestamp() { - const latestBlock = await client.getBlock({ - blockTag: "latest", - includeTransactions: false, - }); + const latestBlock = await client.getBlock(); return latestBlock.timestamp; }, + async writeContractWait< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName< + abi, + "payable" | "nonpayable" + >, + args extends ContractFunctionArgs< + abi, + "payable" | "nonpayable", + functionName + >, + >( + args: WriteContractParameters< + abi, + functionName, + args, + chain, + HDAccount + >, + ) { + const hash = await client.writeContract(args); + + return await client.waitForTransactionReceipt({ hash }); + }, })); diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts index 08fee62b..219f56fc 100644 --- a/packages/test/src/index.ts +++ b/packages/test/src/index.ts @@ -2,4 +2,3 @@ export * from "./fixtures.js"; export * from "./vitest.js"; export * from "./react.js"; export * from "./anvil.js"; -export * from "./utils.js"; diff --git a/packages/test/src/utils.ts b/packages/test/src/utils.ts deleted file mode 100644 index c7a6d151..00000000 --- a/packages/test/src/utils.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const wait = (timeout: number) => - new Promise((resolve) => setTimeout(resolve, timeout));