diff --git a/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap b/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap index d96dcaba..8470195f 100644 --- a/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap +++ b/.forge-snapshots/Base-V3DutchOrder-BaseExecuteSingleWithFee.snap @@ -1 +1 @@ -198870 \ No newline at end of file +199230 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap index 8dd69151..495e8ce3 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap @@ -1 +1 @@ -231296 \ No newline at end of file +232079 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap index 0fed4ea0..60b5d531 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputs.snap @@ -1 +1 @@ -244817 \ No newline at end of file +245939 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap index d7065025..4f3589c3 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchMultipleOutputsDifferentTokens.snap @@ -1 +1 @@ -302243 \ No newline at end of file +303724 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap index 19c71545..1c7b5194 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteBatchNativeOutput.snap @@ -1 +1 @@ -224822 \ No newline at end of file +225606 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap index 67e0047d..0b90e638 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap @@ -1 +1 @@ -165255 \ No newline at end of file +165613 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap index b90f3684..56d72161 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleNativeOutput.snap @@ -1 +1 @@ -150817 \ No newline at end of file +151175 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap index ee1fea4c..8b242c31 100644 --- a/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap +++ b/.forge-snapshots/Base-V3DutchOrder-ExecuteSingleValidation.snap @@ -1 +1 @@ -174571 \ No newline at end of file +174924 \ No newline at end of file diff --git a/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap b/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap index b1fb824b..3e591c1d 100644 --- a/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap +++ b/.forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap @@ -1 +1 @@ -43875 \ No newline at end of file +44248 \ 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 2c159b4f..e0664b0b 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap @@ -1 +1 @@ -169179 \ No newline at end of file +169548 \ 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 9ba5284c..22657848 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap @@ -1 +1 @@ -169260 \ No newline at end of file +169629 \ 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 ab41702e..30e3c90d 100644 --- a/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap +++ b/.forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap @@ -1 +1 @@ -169203 \ No newline at end of file +169572 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecay.snap b/.forge-snapshots/V3-DutchDecay.snap index af355057..bd87d76d 100644 --- a/.forge-snapshots/V3-DutchDecay.snap +++ b/.forge-snapshots/V3-DutchDecay.snap @@ -1 +1 @@ -13351 \ No newline at end of file +13543 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayBounded.snap b/.forge-snapshots/V3-DutchDecayBounded.snap index 1280674f..c73013a3 100644 --- a/.forge-snapshots/V3-DutchDecayBounded.snap +++ b/.forge-snapshots/V3-DutchDecayBounded.snap @@ -1 +1 @@ -1247 \ No newline at end of file +1244 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayFullyDecayed.snap b/.forge-snapshots/V3-DutchDecayFullyDecayed.snap index b56f143c..7b7f3c66 100644 --- a/.forge-snapshots/V3-DutchDecayFullyDecayed.snap +++ b/.forge-snapshots/V3-DutchDecayFullyDecayed.snap @@ -1 +1 @@ -6755 \ No newline at end of file +6857 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap b/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap index 861626b3..07956fc9 100644 --- a/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap +++ b/.forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap @@ -1 +1 @@ -6443 \ No newline at end of file +6545 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNegative.snap b/.forge-snapshots/V3-DutchDecayNegative.snap index f699c943..61a41990 100644 --- a/.forge-snapshots/V3-DutchDecayNegative.snap +++ b/.forge-snapshots/V3-DutchDecayNegative.snap @@ -1 +1 @@ -1309 \ No newline at end of file +1303 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNoDecayYet.snap b/.forge-snapshots/V3-DutchDecayNoDecayYet.snap index b26d92eb..8223ded7 100644 --- a/.forge-snapshots/V3-DutchDecayNoDecayYet.snap +++ b/.forge-snapshots/V3-DutchDecayNoDecayYet.snap @@ -1 +1 @@ -4779 \ No newline at end of file +4773 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap b/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap index b26d92eb..8223ded7 100644 --- a/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap +++ b/.forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap @@ -1 +1 @@ -4779 \ No newline at end of file +4773 \ No newline at end of file diff --git a/.forge-snapshots/V3-DutchDecayRange.snap b/.forge-snapshots/V3-DutchDecayRange.snap index f699c943..61a41990 100644 --- a/.forge-snapshots/V3-DutchDecayRange.snap +++ b/.forge-snapshots/V3-DutchDecayRange.snap @@ -1 +1 @@ -1309 \ No newline at end of file +1303 \ No newline at end of file diff --git a/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap b/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap index 70baed8e..4d37a5ef 100644 --- a/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap +++ b/.forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap @@ -1 +1 @@ -89192 \ No newline at end of file +89288 \ 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 80100ea7..7f6e267f 100644 --- a/.forge-snapshots/V3-MultiPointDutchDecay.snap +++ b/.forge-snapshots/V3-MultiPointDutchDecay.snap @@ -1 +1 @@ -26608 \ No newline at end of file +26905 \ No newline at end of file diff --git a/src/base/BlockNumberish.sol b/src/base/BlockNumberish.sol index 7cfa9928..99c6fdba 100644 --- a/src/base/BlockNumberish.sol +++ b/src/base/BlockNumberish.sol @@ -10,9 +10,12 @@ contract BlockNumberish { // Declare an immutable function type variable for the _getBlockNumberish function function() view returns (uint256) internal immutable _getBlockNumberish; + uint256 private constant ARB_CHAIN_ID = 42161; + address private constant ARB_SYS_ADDRESS = 0x0000000000000000000000000000000000000064; + constructor() { // Set the function to use based on chainid - if (block.chainid == 42161) { + if (block.chainid == ARB_CHAIN_ID) { _getBlockNumberish = _getBlockNumberSyscall; } else { _getBlockNumberish = _getBlockNumber; @@ -21,7 +24,7 @@ contract BlockNumberish { /// @dev Private function to get the block number on arbitrum function _getBlockNumberSyscall() private view returns (uint256) { - return IArbSys(0x0000000000000000000000000000000000000064).arbBlockNumber(); + return IArbSys(ARB_SYS_ADDRESS).arbBlockNumber(); } /// @dev Private function to get the block number using the opcode 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 98b0b5fe..280f559e 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"; import {IArbSys} from "../interfaces/IArbSys.sol"; @@ -18,13 +19,15 @@ 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 + /// @param blockNumberish The current block number /// @param minAmount The minimum amount to decay to /// @param maxAmount The maximum amount to decay to /// @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, @@ -42,8 +45,9 @@ library NonlinearDutchDecayLib { if (decayStartBlock >= blockNumberish || curve.relativeAmounts.length == 0) { return startAmount.bound(minAmount, maxAmount); } - - uint16 blockDelta = uint16(blockNumberish - 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(blockNumberish - decayStartBlock, type(uint16).max)); (uint16 startPoint, uint16 endPoint, int256 relStartAmount, int256 relEndAmount) = locateCurvePosition(curve, blockDelta); // get decay of only the relative amounts @@ -52,13 +56,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..5fb8612a 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,7 +210,7 @@ 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))); diff --git a/src/reactors/V3DutchOrderReactor.sol b/src/reactors/V3DutchOrderReactor.sol index 4f3659e9..fda6cf64 100644 --- a/src/reactors/V3DutchOrderReactor.sol +++ b/src/reactors/V3DutchOrderReactor.sol @@ -15,7 +15,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 @@ -120,21 +120,25 @@ contract V3DutchOrderReactor is BaseReactor, BlockNumberish { int256 gasDeltaGwei = block.basefee.sub(order.startingBaseFee); // Gas increase should increase input - int256 inputDelta = int256(order.baseInput.adjustmentPerGweiBaseFee) * gasDeltaGwei / 1 gwei; - order.baseInput.startAmount = order.baseInput.startAmount.boundedAdd(inputDelta, 0, order.baseInput.maxAmount); - + if (order.baseInput.adjustmentPerGweiBaseFee != 0) { + int256 inputDelta = int256(order.baseInput.adjustmentPerGweiBaseFee) * gasDeltaGwei / 1 gwei; + 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]; - int256 outputDelta = int256(output.adjustmentPerGweiBaseFee) * gasDeltaGwei / 1 gwei; - output.startAmount = output.startAmount.boundedSub(outputDelta, output.minAmount, type(uint256).max); + if (output.adjustmentPerGweiBaseFee != 0) { + int256 outputDelta = int256(output.adjustmentPerGweiBaseFee) * gasDeltaGwei / 1 gwei; + output.startAmount = output.startAmount.boundedSub(outputDelta, output.minAmount, type(uint256).max); + } } } /// @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 68661475..0b20d75e 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"; import {BlockNumberish} from "../../src/base/BlockNumberish.sol"; @@ -638,4 +639,44 @@ contract NonlinearDutchDecayLibTest is Test, GasSnapshot, BlockNumberish { curve, startAmount, decayStartBlock, _getBlockNumberish(), 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, _getBlockNumberish(), minAmount, maxAmount); + assertEq( + decayed, + Math.max(startAmount - decayAmountFuzz, minAmount), + "Should be fully decayed for block delta beyond uint16.max" + ); + } }