diff --git a/README.md b/README.md index 404f5f24b..fdd123600 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Morpho Midnight +# Midnight -Morpho Midnight is a fixed-rate lending protocol based on zero-coupon obligations. +Midnight is a fixed-rate lending protocol based on zero-coupon obligations. ## Whitepaper diff --git a/certora/specs/CollateralBitmap.spec b/certora/specs/CollateralBitmap.spec index e0e6a2a56..35cd6aff7 100644 --- a/certora/specs/CollateralBitmap.spec +++ b/certora/specs/CollateralBitmap.spec @@ -15,7 +15,7 @@ methods { */ function _.price() external => PER_CALLEE_CONSTANT; function TickLib.tickToPrice(uint256 tick) internal returns (uint256) => NONDET; - function IdLib.toId(Midnight.Obligation memory obligation, uint256 chainId, address morpho) internal returns (bytes32) => NONDET; + function IdLib.toId(Midnight.Obligation memory obligation, uint256 chainId, address midnight) internal returns (bytes32) => NONDET; /* Simplify mulDiv reasoning for the solver. We summarize these by ghost functions, i.e., * arbitrary deterministic functions and axiomatize the axioms we need. diff --git a/certora/specs/Healthiness.spec b/certora/specs/Healthiness.spec index c9c91c669..579342802 100644 --- a/certora/specs/Healthiness.spec +++ b/certora/specs/Healthiness.spec @@ -17,7 +17,7 @@ methods { */ function _.price() external => summaryPrice(calledContract) expect(uint256); function TickLib.tickToPrice(uint256 tick) internal returns (uint256) => NONDET; - function IdLib.toId(Midnight.Obligation memory obligation, uint256 chainId, address morpho) internal returns (bytes32) => summaryToId(obligation, chainId, morpho); + function IdLib.toId(Midnight.Obligation memory obligation, uint256 chainId, address midnight) internal returns (bytes32) => summaryToId(obligation, chainId, midnight); /* Summarize mulDivDown and mulDivUp to simplify the verification task. * Use a ghost function that ensures mulDivDown/Up behaves deterministically and @@ -136,9 +136,9 @@ function getGlobalObligation() returns (Midnight.Obligation) { return obligation; } -function summaryToId(Midnight.Obligation obligation, uint256 chainId, address morpho) returns (bytes32) { +function summaryToId(Midnight.Obligation obligation, uint256 chainId, address midnight) returns (bytes32) { bytes32 id; - if (equalsGlobalObligation(obligation) && morpho == currentContract) { + if (equalsGlobalObligation(obligation) && midnight == currentContract) { require id == globalId, "toId() is deterministic"; } else { require id != globalId, "toId() is injective"; diff --git a/certora/specs/NoDivisionByZero.spec b/certora/specs/NoDivisionByZero.spec index 77502247b..d98ae3bcb 100644 --- a/certora/specs/NoDivisionByZero.spec +++ b/certora/specs/NoDivisionByZero.spec @@ -80,9 +80,9 @@ function equalsGlobalObligation(Midnight.Obligation obligation) returns (bool) { return obligation.loanToken == globalObligationLoanToken && obligation.collateralParams.length == globalObligationCollateralLength && collateralMatches(obligation, 0) && collateralMatches(obligation, 1) && collateralMatches(obligation, 2) && obligation.maturity == globalObligationMaturity && obligation.rcfThreshold == globalObligationRcfThreshold && obligation.enterGate == globalObligationEnterGate && obligation.liquidatorGate == globalObligationLiquidatorGate; } -function summaryToId(Midnight.Obligation obligation, uint256 chainId, address morpho) returns (bytes32) { +function summaryToId(Midnight.Obligation obligation, uint256 chainId, address midnight) returns (bytes32) { bytes32 id; - if (equalsGlobalObligation(obligation) && morpho == currentContract) { + if (equalsGlobalObligation(obligation) && midnight == currentContract) { require id == globalId, "toId() is deterministic"; } else { require id != globalId, "toId() is injective"; diff --git a/src/interfaces/IRatifier.sol b/src/interfaces/IRatifier.sol index 32253bc78..c3cfea0f1 100644 --- a/src/interfaces/IRatifier.sol +++ b/src/interfaces/IRatifier.sol @@ -5,5 +5,5 @@ pragma solidity >=0.5.0; import {Offer} from "./IMidnight.sol"; interface IRatifier { - function onRatify(Offer memory offer, bytes32 root, bytes memory data) external returns (bytes32); + function onRatify(Offer memory offer, bytes32 root, bytes memory ratifierData) external returns (bytes32); } diff --git a/src/periphery/TakeBundler.sol b/src/periphery/TakeBundler.sol index eb2dceab3..77025d890 100644 --- a/src/periphery/TakeBundler.sol +++ b/src/periphery/TakeBundler.sol @@ -40,7 +40,7 @@ contract TakeBundler is ITakeBundler { "", receiverIfTakerIsSeller, takes[i].offer, - takes[i].sig, + takes[i].ratifierData, takes[i].root, takes[i].proof ) returns ( @@ -93,7 +93,7 @@ contract TakeBundler is ITakeBundler { "", receiverIfTakerIsSeller, takes[i].offer, - takes[i].sig, + takes[i].ratifierData, takes[i].root, takes[i].proof ) returns ( @@ -142,7 +142,7 @@ contract TakeBundler is ITakeBundler { "", receiverIfTakerIsSeller, takes[i].offer, - takes[i].sig, + takes[i].ratifierData, takes[i].root, takes[i].proof ) returns ( diff --git a/src/periphery/interfaces/ITakeBundler.sol b/src/periphery/interfaces/ITakeBundler.sol index c28696f83..f699228fa 100644 --- a/src/periphery/interfaces/ITakeBundler.sol +++ b/src/periphery/interfaces/ITakeBundler.sol @@ -7,7 +7,7 @@ import {Offer} from "../../interfaces/IMidnight.sol"; struct Take { uint256 units; Offer offer; - bytes sig; + bytes ratifierData; bytes32 root; bytes32[] proof; } diff --git a/src/ratifiers/EcrecoverRatifier.sol b/src/ratifiers/EcrecoverRatifier.sol index 24f4423f3..31bd0ecee 100644 --- a/src/ratifiers/EcrecoverRatifier.sol +++ b/src/ratifiers/EcrecoverRatifier.sol @@ -14,8 +14,8 @@ contract EcrecoverRatifier is IEcrecoverRatifier { MIDNIGHT = _midnight; } - function onRatify(Offer memory offer, bytes32 root, bytes memory data) external view returns (bytes32) { - Signature memory sig = abi.decode(data, (Signature)); + function onRatify(Offer memory offer, bytes32 root, bytes memory ratifierData) external view returns (bytes32) { + Signature memory sig = abi.decode(ratifierData, (Signature)); bytes32 structHash = keccak256(abi.encode(ROOT_TYPEHASH, root)); bytes32 domainSeparator = keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, block.chainid, address(this))); bytes32 digest = keccak256(bytes.concat("\x19\x01", domainSeparator, structHash)); diff --git a/test/AuthorizationTest.sol b/test/AuthorizationTest.sol index 244dfed18..1e94a1552 100644 --- a/test/AuthorizationTest.sol +++ b/test/AuthorizationTest.sol @@ -214,7 +214,9 @@ contract AuthorizationTest is BaseTest { address attacker = makeAddr("attacker"); vm.prank(attacker); vm.expectRevert(IMidnight.TakerUnauthorized.selector); - midnight.take(units, taker, address(0), hex"", address(0), offer, sig([offer]), root([offer]), proof([offer])); + midnight.take( + units, taker, address(0), hex"", address(0), offer, ratifierData([offer]), root([offer]), proof([offer]) + ); } function testTakeAuthorized() public { @@ -240,7 +242,9 @@ contract AuthorizationTest is BaseTest { // Operator can take on behalf of taker vm.prank(operator); - midnight.take(units, taker, address(0), hex"", taker, offer, sig([offer]), root([offer]), proof([offer])); + midnight.take( + units, taker, address(0), hex"", taker, offer, ratifierData([offer]), root([offer]), proof([offer]) + ); assertEq(midnight.debtOf(id, taker), units); } diff --git a/test/BaseTest.sol b/test/BaseTest.sol index d3202defa..aeab71ee6 100644 --- a/test/BaseTest.sol +++ b/test/BaseTest.sol @@ -144,7 +144,9 @@ abstract contract BaseTest is Test { // receiverIfTakerIsSeller param is for taker (when offer.buy == true) // offer.receiverIfMakerIsSeller is for maker (when offer.buy == false) vm.prank(taker); - return midnight.take(units, taker, address(0), hex"", taker, offer, sig([offer]), root([offer]), proof([offer])); + return midnight.take( + units, taker, address(0), hex"", taker, offer, ratifierData([offer]), root([offer]), proof([offer]) + ); } function setupOtherUsers(Obligation memory obligation, uint256 units) internal { @@ -217,7 +219,7 @@ abstract contract BaseTest is Test { return IdLib.toId(obligation, block.chainid, address(midnight)); } - function sig(Offer[1] memory offers, address _signer) internal view returns (bytes memory) { + function ratifierData(Offer[1] memory offers, address _signer) internal view returns (bytes memory) { return abi.encode(signature(root(offers), privateKey[_signer], offers[0].ratifier)); } @@ -260,12 +262,12 @@ abstract contract BaseTest is Test { return _signature; } - function sig(Offer[1] memory offers) internal view returns (bytes memory) { + function ratifierData(Offer[1] memory offers) internal view returns (bytes memory) { bytes32 _root = root(offers); return abi.encode(signature(_root, privateKey[offers[0].maker], offers[0].ratifier)); } - function sig(Offer[2] memory offers) internal view returns (bytes memory) { + function ratifierData(Offer[2] memory offers) internal view returns (bytes memory) { bytes32 _root = root(offers); return abi.encode(signature(_root, privateKey[offers[0].maker], offers[0].ratifier)); } @@ -328,7 +330,7 @@ abstract contract BaseTest is Test { hex"", borrower, borrowerOffer, - sig([borrowerOffer]), + ratifierData([borrowerOffer]), root([borrowerOffer]), proof([borrowerOffer]) ); diff --git a/test/BundlerTest.sol b/test/BundlerTest.sol index b7d0e0d25..7e4ddbc49 100644 --- a/test/BundlerTest.sol +++ b/test/BundlerTest.sol @@ -85,7 +85,11 @@ contract BundlerTest is BaseTest { function testUnauthorized() public { Take[] memory takes = new Take[](1); takes[0] = Take({ - offer: offers[0], units: 100, sig: sig([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) + offer: offers[0], + units: 100, + ratifierData: ratifierData([offers[0]]), + root: root([offers[0]]), + proof: proof([offers[0]]) }); vm.prank(address(0xdead)); @@ -107,14 +111,14 @@ contract BundlerTest is BaseTest { takes[0] = Take({ offer: offers[0], units: offerUnits0, - sig: sig([offers[0]]), + ratifierData: ratifierData([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) }); takes[1] = Take({ offer: offers[1], units: offerUnits1, - sig: sig([offers[1]]), + ratifierData: ratifierData([offers[1]]), root: root([offers[1]]), proof: proof([offers[1]]) }); @@ -157,14 +161,14 @@ contract BundlerTest is BaseTest { takes[0] = Take({ offer: offers[0], units: offerUnits0, - sig: sig([offers[0]]), + ratifierData: ratifierData([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) }); takes[1] = Take({ offer: offers[1], units: offerUnits1, - sig: sig([offers[1]]), + ratifierData: ratifierData([offers[1]]), root: root([offers[1]]), proof: proof([offers[1]]) }); @@ -209,14 +213,14 @@ contract BundlerTest is BaseTest { takes[0] = Take({ offer: offers[0], units: offerUnits0, - sig: sig([offers[0]]), + ratifierData: ratifierData([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) }); takes[1] = Take({ offer: offers[1], units: offerUnits1, - sig: sig([offers[1]]), + ratifierData: ratifierData([offers[1]]), root: root([offers[1]]), proof: proof([offers[1]]) }); @@ -302,14 +306,14 @@ contract BundlerTest is BaseTest { takes[0] = Take({ offer: offers[0], units: offerUnits0, - sig: sig([offers[0]]), + ratifierData: ratifierData([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) }); takes[1] = Take({ offer: offers[1], units: offerUnits1, - sig: sig([offers[1]]), + ratifierData: ratifierData([offers[1]]), root: root([offers[1]]), proof: proof([offers[1]]) }); @@ -352,14 +356,14 @@ contract BundlerTest is BaseTest { takes[0] = Take({ offer: offers[0], units: offerUnits0, - sig: sig([offers[0]]), + ratifierData: ratifierData([offers[0]]), root: root([offers[0]]), proof: proof([offers[0]]) }); takes[1] = Take({ offer: offers[1], units: offerUnits1, - sig: sig([offers[1]]), + ratifierData: ratifierData([offers[1]]), root: root([offers[1]]), proof: proof([offers[1]]) }); diff --git a/test/EcrecoverRatifierTest.sol b/test/EcrecoverRatifierTest.sol index 26cbc00ed..a61727c99 100644 --- a/test/EcrecoverRatifierTest.sol +++ b/test/EcrecoverRatifierTest.sol @@ -27,9 +27,9 @@ contract EcrecoverRatifierTest is BaseTest { function testOnRatifyMakerSigns() public view { Offer memory offer = makeOffer(lender); bytes32 _root = keccak256(abi.encode(offer)); - bytes memory data = signRoot(_root, lender); + bytes memory ratifierData = signRoot(_root, lender); - bytes32 result = ecrecoverRatifier.onRatify(offer, _root, data); + bytes32 result = ecrecoverRatifier.onRatify(offer, _root, ratifierData); assertEq(result, CALLBACK_SUCCESS); } @@ -40,38 +40,38 @@ contract EcrecoverRatifierTest is BaseTest { vm.prank(lender); midnight.setIsAuthorized(lender, borrower, true); - bytes memory data = signRoot(_root, borrower); + bytes memory ratifierData = signRoot(_root, borrower); - bytes32 result = ecrecoverRatifier.onRatify(offer, _root, data); + bytes32 result = ecrecoverRatifier.onRatify(offer, _root, ratifierData); assertEq(result, CALLBACK_SUCCESS); } function testOnRatifyUnauthorizedSigner() public { Offer memory offer = makeOffer(lender); bytes32 _root = keccak256(abi.encode(offer)); - bytes memory data = signRoot(_root, borrower); + bytes memory ratifierData = signRoot(_root, borrower); vm.expectRevert(IEcrecoverRatifier.Unauthorized.selector); - ecrecoverRatifier.onRatify(offer, _root, data); + ecrecoverRatifier.onRatify(offer, _root, ratifierData); } function testOnRatifyInvalidSignature() public { Offer memory offer = makeOffer(lender); bytes32 _root = keccak256(abi.encode(offer)); - bytes memory data = abi.encode(Signature({v: 27, r: bytes32(uint256(1)), s: bytes32(uint256(2))})); + bytes memory ratifierData = abi.encode(Signature({v: 27, r: bytes32(uint256(1)), s: bytes32(uint256(2))})); vm.expectRevert(IEcrecoverRatifier.Unauthorized.selector); - ecrecoverRatifier.onRatify(offer, _root, data); + ecrecoverRatifier.onRatify(offer, _root, ratifierData); } function testOnRatifyWrongRoot() public { Offer memory offer = makeOffer(lender); bytes32 _root = keccak256(abi.encode(offer)); - bytes memory data = signRoot(_root, lender); + bytes memory ratifierData = signRoot(_root, lender); bytes32 wrongRoot = keccak256("wrong"); vm.expectRevert(IEcrecoverRatifier.Unauthorized.selector); - ecrecoverRatifier.onRatify(offer, wrongRoot, data); + ecrecoverRatifier.onRatify(offer, wrongRoot, ratifierData); } function testOnRatifyRevokeAuthorizationInvalidates() public { @@ -81,16 +81,16 @@ contract EcrecoverRatifierTest is BaseTest { vm.prank(lender); midnight.setIsAuthorized(lender, borrower, true); - bytes memory data = signRoot(_root, borrower); + bytes memory ratifierData = signRoot(_root, borrower); // Works while authorized. - ecrecoverRatifier.onRatify(offer, _root, data); + ecrecoverRatifier.onRatify(offer, _root, ratifierData); // Revoke. vm.prank(lender); midnight.setIsAuthorized(lender, borrower, false); vm.expectRevert(IEcrecoverRatifier.Unauthorized.selector); - ecrecoverRatifier.onRatify(offer, _root, data); + ecrecoverRatifier.onRatify(offer, _root, ratifierData); } } diff --git a/test/TakeTest.sol b/test/TakeTest.sol index a7b71f4ce..af37fc8f3 100644 --- a/test/TakeTest.sol +++ b/test/TakeTest.sol @@ -896,7 +896,15 @@ contract TakeTest is BaseTest { vm.expectRevert(IMidnight.InvalidProof.selector); vm.prank(borrower); midnight.take( - 100, borrower, address(0), hex"", borrower, lenderOffer, sig([lenderOffer]), invalidRoot, new bytes32[](0) + 100, + borrower, + address(0), + hex"", + borrower, + lenderOffer, + ratifierData([lenderOffer]), + invalidRoot, + new bytes32[](0) ); } @@ -937,7 +945,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer], vm.addr(otherPrivateKey)), + ratifierData([lenderOffer], vm.addr(otherPrivateKey)), root([lenderOffer]), proof([lenderOffer]) ); @@ -967,7 +975,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer], vm.addr(otherPrivateKey)), + ratifierData([lenderOffer], vm.addr(otherPrivateKey)), root([lenderOffer]), proof([lenderOffer]) ); @@ -980,7 +988,15 @@ contract TakeTest is BaseTest { vm.expectRevert(IMidnight.InvalidProof.selector); vm.prank(borrower); midnight.take( - 100, borrower, address(0), hex"", borrower, lenderOffer, sig([lenderOffer]), root([lenderOffer]), _path + 100, + borrower, + address(0), + hex"", + borrower, + lenderOffer, + ratifierData([lenderOffer]), + root([lenderOffer]), + _path ); } @@ -996,7 +1012,7 @@ contract TakeTest is BaseTest { hex"", borrower, lenderOffer, - sig([lenderOffer, otherOffer]), + ratifierData([lenderOffer, otherOffer]), root([lenderOffer, otherOffer]), _path ); @@ -1017,7 +1033,7 @@ contract TakeTest is BaseTest { hex"", borrower, lenderOffer, - sig([lenderOffer, otherOffer]), + ratifierData([lenderOffer, otherOffer]), root([lenderOffer, otherOffer]), proof([lenderOffer, otherOffer]) ); @@ -1047,7 +1063,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1088,7 +1104,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer], vm.addr(otherSecretKey)), + ratifierData([lenderOffer], vm.addr(otherSecretKey)), root([lenderOffer]), proof([lenderOffer]) ); @@ -1120,7 +1136,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer], vm.addr(otherSecretKey)), + ratifierData([lenderOffer], vm.addr(otherSecretKey)), root([lenderOffer]), proof([lenderOffer]) ); @@ -1147,7 +1163,7 @@ contract TakeTest is BaseTest { hex"", sender, lenderOffer, - sig([lenderOffer], vm.addr(signerPrivateKey)), + ratifierData([lenderOffer], vm.addr(signerPrivateKey)), root([lenderOffer]), proof([lenderOffer]) ); @@ -1167,7 +1183,7 @@ contract TakeTest is BaseTest { hex"", taker, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1184,7 +1200,7 @@ contract TakeTest is BaseTest { hex"", taker, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1205,7 +1221,7 @@ contract TakeTest is BaseTest { hex"", taker, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1257,7 +1273,7 @@ contract TakeTest is BaseTest { abi.encode(0, collateral), borrower, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1289,7 +1305,7 @@ contract TakeTest is BaseTest { abi.encode(0, collateral, repaidUnits), borrower, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1322,7 +1338,7 @@ contract TakeTest is BaseTest { callback.prepare( lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]), units, @@ -1339,7 +1355,7 @@ contract TakeTest is BaseTest { "", borrower, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1370,7 +1386,7 @@ contract TakeTest is BaseTest { hex"", borrower, lenderOffer, - sig([lenderOffer]), + ratifierData([lenderOffer]), root([lenderOffer]), proof([lenderOffer]) ); @@ -1412,7 +1428,7 @@ contract TakeTest is BaseTest { abi.encode(address(loanToken), assets), address(0), borrowerOffer, - sig([borrowerOffer]), + ratifierData([borrowerOffer]), root([borrowerOffer]), proof([borrowerOffer]) ); @@ -1523,7 +1539,7 @@ contract TakeTest is BaseTest { hex"", address(0), borrowerOffer, - sig([borrowerOffer]), + ratifierData([borrowerOffer]), root([borrowerOffer]), proof([borrowerOffer]) ); @@ -1556,7 +1572,9 @@ contract TakeTest is BaseTest { vm.expectRevert(IMidnight.BuyerPendingFeeExceedsCredit.selector); vm.prank(lender); - midnight.take(units, lender, address(0), hex"", lender, bOffer, sig([bOffer]), root([bOffer]), proof([bOffer])); + midnight.take( + units, lender, address(0), hex"", lender, bOffer, ratifierData([bOffer]), root([bOffer]), proof([bOffer]) + ); } } @@ -1798,11 +1816,11 @@ contract RatifyCallback is IRatifier { return _recordedOffer; } - function onRatify(Offer memory offer, bytes32 root, bytes memory data) external returns (bytes32) { + function onRatify(Offer memory offer, bytes32 root, bytes memory ratifierData) external returns (bytes32) { _recordedOffer = offer; - if (data.length > 0) { - Signature memory signature = abi.decode(data, (Signature)); + if (ratifierData.length > 0) { + Signature memory signature = abi.decode(ratifierData, (Signature)); bytes32 structHash = keccak256(abi.encode(ROOT_TYPEHASH, root)); bytes32 domainSeparator = keccak256(abi.encode(EIP712_DOMAIN_TYPEHASH, block.chainid, address(this))); bytes32 digest = keccak256(bytes.concat("\x19\x01", domainSeparator, structHash));