diff --git a/.github/workflows/hardhat.yml b/.github/workflows/hardhat.yml index 1b2dc0be..efe197ee 100644 --- a/.github/workflows/hardhat.yml +++ b/.github/workflows/hardhat.yml @@ -23,3 +23,8 @@ jobs: - name: Build contracts & package with hardhat uses: ./.github/actions/build-hardhat + + - name: Run hardhat tests + run: yarn test:hardhat + env: + ALCHEMY_KEY: ${{ secrets.ALCHEMY_KEY }} diff --git a/.gitmodules b/.gitmodules index 98acf80c..9341c727 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "lib/murky"] path = lib/murky url = https://github.com/dmfxyz/murky +[submodule "lib/morpho-blue-irm"] + path = lib/morpho-blue-irm + url = https://github.com/morpho-labs/morpho-blue-irm diff --git a/lib/morpho-blue-irm b/lib/morpho-blue-irm new file mode 160000 index 00000000..73eddf9e --- /dev/null +++ b/lib/morpho-blue-irm @@ -0,0 +1 @@ +Subproject commit 73eddf9e09731b75255b51d1ee14a321f5586b42 diff --git a/src/mocks/AdaptiveCurveIrmMock.sol b/src/mocks/AdaptiveCurveIrmMock.sol new file mode 100644 index 00000000..3c6e4e6b --- /dev/null +++ b/src/mocks/AdaptiveCurveIrmMock.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {AdaptiveCurveIrm} from "../../lib/morpho-blue-irm/src/AdaptiveCurveIrm.sol"; + +int256 constant _CURVE_STEEPNESS = 4 ether; +int256 constant _ADJUSTMENT_SPEED = int256(50 ether) / 365 days; +int256 constant _TARGET_UTILIZATION = 0.9 ether; +int256 constant _INITIAL_RATE_AT_TARGET = int256(0.01 ether) / 365 days; + +contract AdaptiveCurveIrmMock is AdaptiveCurveIrm { + constructor(address morpho) + AdaptiveCurveIrm(morpho, _CURVE_STEEPNESS, _ADJUSTMENT_SPEED, _TARGET_UTILIZATION, _INITIAL_RATE_AT_TARGET) + {} +} diff --git a/test/hardhat/EthereumBundler.spec.ts b/test/hardhat/EthereumBundler.spec.ts index 3055a51a..5ac85303 100644 --- a/test/hardhat/EthereumBundler.spec.ts +++ b/test/hardhat/EthereumBundler.spec.ts @@ -1,7 +1,7 @@ import { AbiCoder, MaxUint256, Signature, keccak256, toBigInt, TypedDataDomain, TypedDataField } from "ethers"; import hre from "hardhat"; import { BundlerAction } from "pkg"; -import { ERC20Mock, ERC4626Mock, EthereumBundler, MorphoMock, OracleMock, IrmMock } from "types"; +import { ERC20Mock, ERC4626Mock, EthereumBundler, MorphoMock, OracleMock, AdaptiveCurveIrmMock } from "types"; import { MarketParamsStruct } from "types/lib/morpho-blue/src/Morpho"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; @@ -25,32 +25,36 @@ const permit2Config: TypedDataConfig = { verifyingContract: permit2Address, }, types: { - PermitTransferFrom: [ + PermitSingle: [ { - name: "permitted", - type: "TokenPermissions", + name: "details", + type: "PermitDetails", }, { name: "spender", type: "address", }, { - name: "nonce", - type: "uint256", - }, - { - name: "deadline", + name: "sigDeadline", type: "uint256", }, ], - TokenPermissions: [ + PermitDetails: [ { name: "token", type: "address", }, { name: "amount", - type: "uint256", + type: "uint160", + }, + { + name: "expiration", + type: "uint48", + }, + { + name: "nonce", + type: "uint48", }, ], }, @@ -130,7 +134,7 @@ describe("EthereumBundler", () => { let loan: ERC20Mock; let collateral: ERC20Mock; let oracle: OracleMock; - let irm: IrmMock; + let irm: AdaptiveCurveIrmMock; let morphoAuthorizationConfig: TypedDataConfig; @@ -174,9 +178,9 @@ describe("EthereumBundler", () => { const morphoAddress = await morpho.getAddress(); - const IrmFactory = await hre.ethers.getContractFactory("IrmMock", admin); + const AdaptiveCurveIrmFactory = await hre.ethers.getContractFactory("AdaptiveCurveIrmMock", admin); - irm = await IrmFactory.deploy(); + irm = await AdaptiveCurveIrmFactory.deploy(morphoAddress); morphoAuthorizationConfig = { domain: { chainId: "0x1", verifyingContract: morphoAddress }, @@ -226,7 +230,7 @@ describe("EthereumBundler", () => { hre.tracer.nameTags[collateralAddress] = "Collateral"; hre.tracer.nameTags[loanAddress] = "Loan"; hre.tracer.nameTags[oracleAddress] = "Oracle"; - hre.tracer.nameTags[irmAddress] = "Irm"; + hre.tracer.nameTags[irmAddress] = "AdaptiveCurveIrm"; hre.tracer.nameTags[bundlerAddress] = "EthereumBundler"; }); @@ -250,14 +254,17 @@ describe("EthereumBundler", () => { deadline: MAX_UINT48, }; - const permit2TransferFrom = { - permitted: { - token: await collateral.getAddress(), + const collateralAddress = await collateral.getAddress(); + + const approve2 = { + details: { + token: collateralAddress, amount: assets, + nonce: 0n, + expiration: MAX_UINT48, }, spender: bundlerAddress, - nonce: 0n, - deadline: MAX_UINT48, + sigDeadline: MAX_UINT48, }; await collateral.connect(borrower).approve(permit2Address, MaxUint256); @@ -278,14 +285,14 @@ describe("EthereumBundler", () => { ), false, ), - BundlerAction.permit2TransferFrom( - permit2TransferFrom, - Signature.from( - await borrower.signTypedData(permit2Config.domain, permit2Config.types, permit2TransferFrom), - ), + BundlerAction.approve2( + approve2, + Signature.from(await borrower.signTypedData(permit2Config.domain, permit2Config.types, approve2)), + false, ), + BundlerAction.transferFrom2(collateralAddress, assets), BundlerAction.morphoSupplyCollateral(marketParams, assets, borrower.address, []), - BundlerAction.morphoBorrow(marketParams, assets / 2n, 0, MaxUint256, borrower.address), + BundlerAction.morphoBorrow(marketParams, assets / 2n, 0, borrower.address, borrower.address), ]); } }); @@ -297,15 +304,17 @@ describe("EthereumBundler", () => { const supplier = suppliers[i]; const assets = BigInt.WAD * toBigInt(1 + Math.floor(random() * 100)); + const collateralAddress = await collateral.getAddress(); - const permit2TransferFrom = { - permitted: { - token: await collateral.getAddress(), + const approve2 = { + details: { + token: collateralAddress, amount: assets, + expiration: MAX_UINT48, + nonce: 0n, }, spender: bundlerAddress, - nonce: 0n, - deadline: MAX_UINT48, + sigDeadline: MAX_UINT48, }; await collateral.connect(supplier).approve(permit2Address, MaxUint256); @@ -315,12 +324,12 @@ describe("EthereumBundler", () => { await bundler .connect(supplier) .multicall([ - BundlerAction.permit2TransferFrom( - permit2TransferFrom, - Signature.from( - await supplier.signTypedData(permit2Config.domain, permit2Config.types, permit2TransferFrom), - ), + BundlerAction.approve2( + approve2, + Signature.from(await supplier.signTypedData(permit2Config.domain, permit2Config.types, approve2)), + false, ), + BundlerAction.transferFrom2(collateralAddress, assets), BundlerAction.erc4626Deposit(erc4626Address, assets, 0, supplier.address), ]); }