Skip to content

Commit

Permalink
Merge branch 'main' into audit-v3-18
Browse files Browse the repository at this point in the history
  • Loading branch information
alanhwu committed Oct 16, 2024
2 parents f218ffa + f370eab commit c0c13cf
Show file tree
Hide file tree
Showing 30 changed files with 99 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
200713
199172
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
235050
231968
Original file line number Diff line number Diff line change
@@ -1 +1 @@
249575
245818
Original file line number Diff line number Diff line change
@@ -1 +1 @@
308024
303592
Original file line number Diff line number Diff line change
@@ -1 +1 @@
228576
225494
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
167096
165555
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152658
151117
Original file line number Diff line number Diff line change
@@ -1 +1 @@
176407
174866
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
45732
44191
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-V3-ExclusiveFiller.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171021
169490
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-V3-InputOverride.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171102
169571
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-V3-OutputOverride.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171045
169514
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
13179
13371
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayBounded.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1209
1193
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayFullyDecayed.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6677
6779
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6365
6467
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1271
1265
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNoDecayYet.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4703
4697
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4703
4697
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1271
1265
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
88354
88450
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-LocateCurvePositionMulti.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20492
20330
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-LocateCurvePositionSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6860
6830
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-MultiPointDutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
26242
26539
2 changes: 2 additions & 0 deletions src/lib/MathExt.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand Down
32 changes: 19 additions & 13 deletions src/lib/NonlinearDutchDecayLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib/V3DutchOrderLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -210,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 view returns (bytes32) {
return keccak256(abi.encodePacked(orderHash, block.chainid, abi.encode(order.cosignerData)));
Expand Down
18 changes: 11 additions & 7 deletions src/reactors/V3DutchOrderReactor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -113,21 +113,25 @@ contract V3DutchOrderReactor is BaseReactor {
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) {
Expand Down
2 changes: 1 addition & 1 deletion src/types/Uint16Array.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
41 changes: 41 additions & 0 deletions test/lib/NonLinearDutchDecayLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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"
);
}
}

0 comments on commit c0c13cf

Please sign in to comment.