diff --git a/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap b/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap index 4189ab1c..5cd8344c 100644 --- a/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap +++ b/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap @@ -1 +1 @@ -198680 \ No newline at end of file +199172 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap index 6d9b5398..26009a86 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap @@ -1 +1 @@ -230916 \ No newline at end of file +231968 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap index db264c79..9540249d 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap @@ -1 +1 @@ -244376 \ No newline at end of file +245818 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap index 5fa802f2..432d427d 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap @@ -1 +1 @@ -301741 \ No newline at end of file +303592 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap index 18ceec9a..e97b81f9 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap @@ -1 +1 @@ -224442 \ No newline at end of file +225494 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap index dac86684..9cee8b71 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap @@ -1 +1 @@ -165065 \ No newline at end of file +165555 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap index 462a84ec..7ae7e611 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap @@ -1 +1 @@ -150627 \ No newline at end of file +151117 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap index 1bf89fbb..188a54c0 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap @@ -1 +1 @@ -174381 \ No newline at end of file +174866 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap b/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap index c7b7e542..e5308bcc 100644 --- a/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap +++ b/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap @@ -1 +1 @@ -43685 \ No newline at end of file +44191 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap b/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap index 751f1997..78251840 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap @@ -1 +1 @@ -168989 \ No newline at end of file +169490 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap b/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap index 1b58f6bb..c5c65d5c 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap @@ -1 +1 @@ -169070 \ No newline at end of file +169571 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap b/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap index 8514e5c2..f1a4bb72 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap @@ -1 +1 @@ -169013 \ No newline at end of file +169514 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecay.snap b/.forge-snapshots/V3-DutchDecay.snap index 2b163bad..284a734f 100644 --- a/.forge-snapshots/V3-DutchDecay.snap +++ b/.forge-snapshots/V3-DutchDecay.snap @@ -1 +1 @@ -13179 \ No newline at end of file +13371 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayBounded.snap b/.forge-snapshots/V3-DutchDecayBounded.snap index 1f168c59..77e48ce7 100644 --- a/.forge-snapshots/V3-DutchDecayBounded.snap +++ b/.forge-snapshots/V3-DutchDecayBounded.snap @@ -1 +1 @@ -1199 \ No newline at end of file +1193 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayFullyDecayed.snap b/.forge-snapshots/V3-DutchDecayFullyDecayed.snap index e5e37b03..e2b27312 100644 --- a/.forge-snapshots/V3-DutchDecayFullyDecayed.snap +++ b/.forge-snapshots/V3-DutchDecayFullyDecayed.snap @@ -1 +1 @@ -6677 \ No newline at end of file +6779 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap b/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap index 50d06af6..67e488ea 100644 --- a/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap +++ b/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap @@ -1 +1 @@ -6365 \ No newline at end of file +6467 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNegative.snap b/.forge-snapshots/V3-DutchDecayNegative.snap index 11b9a892..34201494 100644 --- a/.forge-snapshots/V3-DutchDecayNegative.snap +++ b/.forge-snapshots/V3-DutchDecayNegative.snap @@ -1 +1 @@ -1271 \ No newline at end of file +1265 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNoDecayYet.snap b/.forge-snapshots/V3-DutchDecayNoDecayYet.snap index 9b08816d..222192ce 100644 --- a/.forge-snapshots/V3-DutchDecayNoDecayYet.snap +++ b/.forge-snapshots/V3-DutchDecayNoDecayYet.snap @@ -1 +1 @@ -4703 \ No newline at end of file +4697 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap b/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap index 9b08816d..222192ce 100644 --- a/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap +++ b/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap @@ -1 +1 @@ -4703 \ No newline at end of file +4697 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayRange.snap b/.forge-snapshots/V3-DutchDecayRange.snap index 11b9a892..34201494 100644 --- a/.forge-snapshots/V3-DutchDecayRange.snap +++ b/.forge-snapshots/V3-DutchDecayRange.snap @@ -1 +1 @@ -1271 \ No newline at end of file +1265 \ No newline at end of file diff --git a/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap b/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap index c1250b2e..6dffa512 100644 --- a/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap +++ b/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap @@ -1 +1 @@ -88354 \ No newline at end of file +88450 \ No newline at end of file diff --git a/.forge-snapshots/V3-LocateCurvePositionMulti.snap b/.forge-snapshots/V3-LocateCurvePositionMulti.snap index 73b08ba1..958c8b94 100644 --- a/.forge-snapshots/V3-LocateCurvePositionMulti.snap +++ b/.forge-snapshots/V3-LocateCurvePositionMulti.snap @@ -1 +1 @@ -20492 \ No newline at end of file +20330 \ No newline at end of file diff --git a/.forge-snapshots/V3-LocateCurvePositionSingle.snap b/.forge-snapshots/V3-LocateCurvePositionSingle.snap index 9726dc60..4a2277e6 100644 --- a/.forge-snapshots/V3-LocateCurvePositionSingle.snap +++ b/.forge-snapshots/V3-LocateCurvePositionSingle.snap @@ -1 +1 @@ -6860 \ No newline at end of file +6830 \ No newline at end of file diff --git a/.forge-snapshots/V3-MultiPointDutchDecay.snap b/.forge-snapshots/V3-MultiPointDutchDecay.snap index 81237bad..1a263175 100644 --- a/.forge-snapshots/V3-MultiPointDutchDecay.snap +++ b/.forge-snapshots/V3-MultiPointDutchDecay.snap @@ -1 +1 @@ -26242 \ No newline at end of file +26539 \ No newline at end of file diff --git a/src/lib/MathExt.sol b/src/lib/MathExt.sol index 02a2a786..89bdf494 100644 --- a/src/lib/MathExt.sol +++ b/src/lib/MathExt.sol @@ -23,6 +23,7 @@ library MathExt { /// @param min The minimum bound for the result. /// @param max The maximum bound for the result. /// @return r The result of the bounded addition. + /// @dev This function reverts when `b == type(int256).min` due to integer overflow. function boundedAdd(uint256 a, int256 b, uint256 min, uint256 max) internal pure returns (uint256 r) { r = boundedSub(a, 0 - b, min, max); } @@ -33,6 +34,7 @@ library MathExt { /// @param min The minimum bound for the result. /// @param max The maximum bound for the result. /// @return r The result of the bounded subtraction. + /// @dev This function reverts when `b == type(int256).min` due to integer overflow. function boundedSub(uint256 a, int256 b, uint256 min, uint256 max) internal pure returns (uint256 r) { if (b < 0) { // If b is negative, add its absolute value to a diff --git a/src/lib/NonlinearDutchDecayLib.sol b/src/lib/NonlinearDutchDecayLib.sol index b979a883..bdeaa5f1 100644 --- a/src/lib/NonlinearDutchDecayLib.sol +++ b/src/lib/NonlinearDutchDecayLib.sol @@ -5,6 +5,7 @@ import {OutputToken, InputToken} from "../base/ReactorStructs.sol"; import {V3DutchOutput, V3DutchInput, NonlinearDutchDecay} from "../lib/V3DutchOrderLib.sol"; import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; import {MathExt} from "./MathExt.sol"; +import {Math} from "openzeppelin-contracts/utils/math/Math.sol"; import {Uint16ArrayLibrary, Uint16Array, fromUnderlying} from "../types/Uint16Array.sol"; import {DutchDecayLib} from "./DutchDecayLib.sol"; @@ -17,11 +18,12 @@ library NonlinearDutchDecayLib { /// @notice thrown when the decay curve is invalid error InvalidDecayCurve(); - /// @notice locates the current position on the curve and calculates the decay - /// @param curve The curve to search - /// @param startAmount The absolute start amount - /// @param decayStartBlock The absolute start block of the decay + /// @notice Calculates the decayed amount based on the current block and the defined curve + /// @param curve The nonlinear decay curve definition + /// @param startAmount The initial amount at the start of the decay + /// @param decayStartBlock The absolute block number when the decay begins /// @dev Expects the relativeBlocks in curve to be strictly increasing + /// @return decayedAmount The amount after applying the decay, bounded by minAmount and maxAmount function decay( NonlinearDutchDecay memory curve, uint256 startAmount, @@ -38,8 +40,9 @@ library NonlinearDutchDecayLib { if (decayStartBlock >= block.number || curve.relativeAmounts.length == 0) { return startAmount.bound(minAmount, maxAmount); } - - uint16 blockDelta = uint16(block.number - decayStartBlock); + // If the blockDelta is larger than type(uint16).max, a downcast overflow will occur + // We prevent this by capping the blockDelta to type(uint16).max to express a full decay + uint16 blockDelta = uint16(Math.min(block.number - decayStartBlock, type(uint16).max)); (uint16 startPoint, uint16 endPoint, int256 relStartAmount, int256 relEndAmount) = locateCurvePosition(curve, blockDelta); // get decay of only the relative amounts @@ -48,13 +51,16 @@ library NonlinearDutchDecayLib { return startAmount.boundedSub(curveDelta, minAmount, maxAmount); } - /// @notice Locates the current position on the curve - /// @param curve The curve to search - /// @param currentRelativeBlock The current relative position - /// @return startPoint The relative block before the current position - /// @return endPoint The relative block after the current position - /// @return startAmount The relative amount before the current position - /// @return endAmount The relative amount after the current position + /// @notice Locates the current position on the decay curve based on the elapsed blocks + /// @param curve The nonlinear decay curve definition + /// @param currentRelativeBlock The number of blocks elapsed since decayStartBlock + /// @return startPoint The relative block number of the previous curve point + /// @return endPoint The relative block number of the next curve point + /// @return startAmount The relative change from initial amount at the previous curve point + /// @return endAmount The relative change from initial amount at the next curve point + /// @dev The returned amounts are changes relative to the initial startAmount, not absolute values + /// @dev If currentRelativeBlock is before the first curve point, startPoint and startAmount will be 0 + /// @dev If currentRelativeBlock is after the last curve point, both points will be the last curve point function locateCurvePosition(NonlinearDutchDecay memory curve, uint16 currentRelativeBlock) internal pure diff --git a/src/lib/V3DutchOrderLib.sol b/src/lib/V3DutchOrderLib.sol index 41798218..38cef7e4 100644 --- a/src/lib/V3DutchOrderLib.sol +++ b/src/lib/V3DutchOrderLib.sol @@ -95,7 +95,8 @@ library V3DutchOrderLib { "uint256 maxAmount,", "uint256 adjustmentPerGweiBaseFee)" ); - bytes32 internal constant V3_DUTCH_INPUT_TYPE_HASH = keccak256(V3_DUTCH_INPUT_TYPE); + bytes32 internal constant V3_DUTCH_INPUT_TYPE_HASH = + keccak256(abi.encodePacked(V3_DUTCH_INPUT_TYPE, NON_LINEAR_DECAY_TYPE)); bytes internal constant V3_DUTCH_OUTPUT_TYPE = abi.encodePacked( "V3DutchOutput(", "address token,", @@ -105,7 +106,8 @@ library V3DutchOrderLib { "uint256 minAmount,", "uint256 adjustmentPerGweiBaseFee)" ); - bytes32 internal constant V3_DUTCH_OUTPUT_TYPE_HASH = keccak256(V3_DUTCH_OUTPUT_TYPE); + bytes32 internal constant V3_DUTCH_OUTPUT_TYPE_HASH = + keccak256(abi.encodePacked(V3_DUTCH_OUTPUT_TYPE, NON_LINEAR_DECAY_TYPE)); bytes internal constant NON_LINEAR_DECAY_TYPE = abi.encodePacked("NonlinearDutchDecay(", "uint256 relativeBlocks,", "int256[] relativeAmounts)"); bytes32 internal constant NON_LINEAR_DECAY_TYPE_HASH = keccak256(NON_LINEAR_DECAY_TYPE); @@ -208,9 +210,9 @@ library V3DutchOrderLib { } /// @notice get the digest of the cosigner data - /// @param order the priorityOrder + /// @param order the V3DutchOrder /// @param orderHash the hash of the order - function cosignerDigest(V3DutchOrder memory order, bytes32 orderHash) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(orderHash, abi.encode(order.cosignerData))); + function cosignerDigest(V3DutchOrder memory order, bytes32 orderHash) internal view returns (bytes32) { + return keccak256(abi.encodePacked(orderHash, block.chainid, abi.encode(order.cosignerData))); } } diff --git a/src/reactors/V3DutchOrderReactor.sol b/src/reactors/V3DutchOrderReactor.sol index ac20f2a6..afdd46cf 100644 --- a/src/reactors/V3DutchOrderReactor.sol +++ b/src/reactors/V3DutchOrderReactor.sol @@ -14,7 +14,7 @@ import {Math} from "openzeppelin-contracts/utils/math/Math.sol"; import {CosignerLib} from "../lib/CosignerLib.sol"; /// @notice Reactor for V3 dutch orders -/// @dev V3 orders must be cosigned by the specified cosigner to override starting block and value +/// @dev V3 orders must be cosigned by the specified cosigner to set the starting block and override the value /// @dev resolution behavior: /// - If cosignature is invalid or not from specified cosigner, revert /// - If inputAmount is 0, then use baseInput @@ -111,17 +111,21 @@ contract V3DutchOrderReactor is BaseReactor { function _updateWithGasAdjustment(V3DutchOrder memory order) internal view { // Positive means an increase in gas int256 gasDeltaWei = block.basefee.sub(order.startingBaseFee); - - // Round in favor of swapper - int256 inputDelta = _computeDelta(order.baseInput.adjustmentPerGweiBaseFee, gasDeltaWei); - order.baseInput.startAmount = order.baseInput.startAmount.boundedAdd(inputDelta, 0, order.baseInput.maxAmount); - + // Gas increase should increase input + if (order.baseInput.adjustmentPerGweiBaseFee != 0) { + // Round in favor of swapper + int256 inputDelta = _computeDelta(order.baseInput.adjustmentPerGweiBaseFee, gasDeltaWei); + order.baseInput.startAmount = order.baseInput.startAmount.boundedAdd(inputDelta, 0, order.baseInput.maxAmount); + } + // Gas increase should decrease output uint256 outputsLength = order.baseOutputs.length; for (uint256 i = 0; i < outputsLength; i++) { V3DutchOutput memory output = order.baseOutputs[i]; - // Round in favor of swapper - int256 outputDelta = _computeDelta(output.adjustmentPerGweiBaseFee, gasDeltaWei); - output.startAmount = output.startAmount.boundedSub(outputDelta, output.minAmount, type(uint256).max); + if (output.adjustmentPerGweiBaseFee != 0) { + // Round in favor of swapper + int256 outputDelta = _computeDelta(output.adjustmentPerGweiBaseFee, gasDeltaWei); + output.startAmount = output.startAmount.boundedSub(outputDelta, output.minAmount, type(uint256).max); + } } } @@ -137,7 +141,7 @@ contract V3DutchOrderReactor is BaseReactor { /// @notice validate the dutch order fields /// - deadline must have not passed - /// - cosigner is valid if specified + /// - cosigner must always be provided and sign the cosignerData /// @dev Throws if the order is invalid function _validateOrder(bytes32 orderHash, V3DutchOrder memory order) internal view { if (order.info.deadline < block.timestamp) { diff --git a/src/types/Uint16Array.sol b/src/types/Uint16Array.sol index 0ecb75a7..61438038 100644 --- a/src/types/Uint16Array.sol +++ b/src/types/Uint16Array.sol @@ -37,7 +37,7 @@ library Uint16ArrayLibrary { } unchecked { uint256 shiftAmount = n * 16; - uint16 result = uint16((Uint16Array.unwrap(packedData) >> shiftAmount) & 0xFFFF); + uint16 result = uint16(Uint16Array.unwrap(packedData) >> shiftAmount); return result; } } diff --git a/test/lib/EIP712.t.sol b/test/lib/EIP712.t.sol new file mode 100644 index 00000000..2b74e690 --- /dev/null +++ b/test/lib/EIP712.t.sol @@ -0,0 +1,46 @@ +import {Test} from "forge-std/Test.sol"; +import {NonlinearDutchDecay, V3DutchOrderLib, V3DutchOutput} from "../../src/lib/V3DutchOrderLib.sol"; + +contract EIP712Test is Test { + function test_NonlinearDutchDecayHash() public pure { + assertEq( + V3DutchOrderLib.NON_LINEAR_DECAY_TYPE_HASH, + hex"30c39ae6ecb284279579f99803ba5d7b54275a8a6a04180056b5031b8c19a01a" + ); + + int256[] memory amounts = new int256[](1); + amounts[0] = 1; + NonlinearDutchDecay memory curve = NonlinearDutchDecay(1, amounts); + assertEq(V3DutchOrderLib.hash(curve), hex"ad28931e960b684a49cdf1aca21bd966df5bac39996c5a0615c0a54f2f22a06f"); + } + + function test_V3DutchInputHash() public pure { + assertEq( + V3DutchOrderLib.V3_DUTCH_INPUT_TYPE_HASH, + hex"2cc4ccc271072d8b406616a16a6e9a3935dea10f0eb920f44737e1855ecc68eb" + ); + } + + function test_V3DutchOutputHash() public pure { + assertEq( + V3DutchOrderLib.V3_DUTCH_OUTPUT_TYPE_HASH, + hex"7fd857ffad1736e72f90e17c9d15cabe562b86b45c6e618ae0e7fa92c4a6fde9" + ); + + address token = address(0); + uint256 startAmount = 21; + int256[] memory amounts = new int256[](1); + amounts[0] = 1; + NonlinearDutchDecay memory curve = NonlinearDutchDecay(1, amounts); + address recipient = address(0); + uint256 minAmount = 20; + uint256 adjustmentPerGweiBaseFee = 0; + V3DutchOutput memory output = + V3DutchOutput(token, startAmount, curve, recipient, minAmount, adjustmentPerGweiBaseFee); + assertEq(V3DutchOrderLib.hash(output), hex"c57ac5e0436939ec593af412dde4a05d4972a0a8a56bbdb63ca7cd949c5326e2"); + } + + function test_OrderTypeHash() public pure { + assertEq(V3DutchOrderLib.ORDER_TYPE_HASH, hex"186c8af0344af94faab60c9dc413f68b8ca7aea1aded04a300c7fa35562ed1b7"); + } +} diff --git a/test/lib/NonLinearDutchDecayLib.t.sol b/test/lib/NonLinearDutchDecayLib.t.sol index c4217418..8fa6c522 100644 --- a/test/lib/NonLinearDutchDecayLib.t.sol +++ b/test/lib/NonLinearDutchDecayLib.t.sol @@ -8,6 +8,7 @@ import {DutchDecayLib} from "../../src/lib/DutchDecayLib.sol"; import {NonlinearDutchDecayLib} from "../../src/lib/NonlinearDutchDecayLib.sol"; import {V3DutchOutput, V3DutchInput, NonlinearDutchDecay} from "../../src/lib/V3DutchOrderLib.sol"; import {Uint16Array, toUint256} from "../../src/types/Uint16Array.sol"; +import {Math} from "openzeppelin-contracts/utils/math/Math.sol"; import {ArrayBuilder} from "../util/ArrayBuilder.sol"; import {CurveBuilder} from "../util/CurveBuilder.sol"; @@ -474,4 +475,44 @@ contract NonlinearDutchDecayLibTest is Test, GasSnapshot { vm.expectRevert(NonlinearDutchDecayLib.InvalidDecayCurve.selector); mockNonlinearDutchDecayLibContract.decay(curve, startAmount, decayStartBlock, 1 ether, 1 ether); } + + function testFuzzDutchDecayBeyondUint16Max( + uint16 lastValidBlock, // For curve + uint256 decayAmountFuzz, // For curve + // decay(curve, startAmount, decayStartBlock, minAmount, maxAmount); + uint256 startAmount, + uint256 decayStartBlock, + uint256 minAmount, + uint256 maxAmount, + uint256 currentBlock + ) public { + vm.assume(decayStartBlock < type(uint256).max - type(uint16).max); + vm.assume(lastValidBlock > 0); + vm.assume(startAmount > 0 && startAmount < uint256(type(int256).max)); + vm.assume(maxAmount >= startAmount); + minAmount = bound(minAmount, 0, startAmount); + // bound only takes uint256, so we need to limit decayAmountFuzz to int256.max + // because we cast it to int256 in the decay function + decayAmountFuzz = bound(decayAmountFuzz, minAmount, startAmount); + + // Testing that we get a fully decayed curve instead of overflowed mistake + // This will happen when the block delta is larger than type(uint16).max + vm.assume(currentBlock > decayStartBlock + type(uint16).max); + + uint16[] memory blocks = new uint16[](1); + blocks[0] = lastValidBlock; + + int256[] memory decayAmounts = new int256[](1); + decayAmounts[0] = int256(decayAmountFuzz); + + NonlinearDutchDecay memory curve = CurveBuilder.multiPointCurve(blocks, decayAmounts); + + vm.roll(currentBlock); + uint256 decayed = NonlinearDutchDecayLib.decay(curve, startAmount, decayStartBlock, minAmount, maxAmount); + assertEq( + decayed, + Math.max(startAmount - decayAmountFuzz, minAmount), + "Should be fully decayed for block delta beyond uint16.max" + ); + } } diff --git a/test/reactors/V3DutchOrderReactor.t.sol b/test/reactors/V3DutchOrderReactor.t.sol index fed2cf70..694cca03 100644 --- a/test/reactors/V3DutchOrderReactor.t.sol +++ b/test/reactors/V3DutchOrderReactor.t.sol @@ -1501,8 +1501,8 @@ contract V3DutchOrderTest is PermitSignature, DeployPermit2, BaseReactorTest { (order,) = createAndSignDutchOrder(request); } - function cosignOrder(bytes32 orderHash, CosignerData memory cosignerData) private pure returns (bytes memory sig) { - bytes32 msgHash = keccak256(abi.encodePacked(orderHash, abi.encode(cosignerData))); + function cosignOrder(bytes32 orderHash, CosignerData memory cosignerData) private view returns (bytes memory sig) { + bytes32 msgHash = keccak256(abi.encodePacked(orderHash, block.chainid, abi.encode(cosignerData))); (uint8 v, bytes32 r, bytes32 s) = vm.sign(cosignerPrivateKey, msgHash); sig = bytes.concat(r, s, bytes1(v)); }