Skip to content

Commit

Permalink
audit(14): arbitrum block number (#280)
Browse files Browse the repository at this point in the history
* Use IArbSys for Arbitrum block number fetching

* fmt

* style: forge fmt

* Add BlockNumberish and tests

* nit: comment

* add attribution

* Move MockArbSys to test/util/mock/

* forge fmt

---------

Co-authored-by: Alan Wu <[email protected]>
Co-authored-by: Cody Born <[email protected]>
  • Loading branch information
3 people authored Oct 18, 2024
1 parent 9a35aad commit c145fa4
Show file tree
Hide file tree
Showing 33 changed files with 212 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
199182
199641
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-ExecuteBatch.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
231988
232940
Original file line number Diff line number Diff line change
@@ -1 +1 @@
245843
247008
Original file line number Diff line number Diff line change
@@ -1 +1 @@
303622
305002
Original file line number Diff line number Diff line change
@@ -1 +1 @@
225514
226466
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-ExecuteSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
165565
166023
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151127
151585
Original file line number Diff line number Diff line change
@@ -1 +1 @@
174876
175334
2 changes: 1 addition & 1 deletion .forge-snapshots/Base-V3DutchOrder-RevertInvalidNonce.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
44201
44659
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 @@
169500
169958
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 @@
169581
170039
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 @@
169524
169982
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
17903
19019
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayBounded.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3389
3607
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayFullyDecayed.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9958
10514
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayFullyDecayedNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9672
10229
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3861
4079
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNoDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9977
9503
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNoDecayYet.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7890
8326
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayNoDecayYetNegative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7864
8300
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-DutchDecayRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3861
4079
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-ExtendedMultiPointDutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
103346
109317
2 changes: 1 addition & 1 deletion .forge-snapshots/V3-MultiPointDutchDecay.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
34003
36476
34 changes: 34 additions & 0 deletions src/base/BlockNumberish.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IArbSys} from "../interfaces/IArbSys.sol";

/// @title BlockNumberish
/// A helper contract to get the current block number on different chains
/// inspired by https://github.com/ProjectOpenSea/tstorish/blob/main/src/Tstorish.sol
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 == ARB_CHAIN_ID) {
_getBlockNumberish = _getBlockNumberSyscall;
} else {
_getBlockNumberish = _getBlockNumber;
}
}

/// @dev Private function to get the block number on arbitrum
function _getBlockNumberSyscall() private view returns (uint256) {
return IArbSys(ARB_SYS_ADDRESS).arbBlockNumber();
}

/// @dev Private function to get the block number using the opcode
function _getBlockNumber() private view returns (uint256) {
return block.number;
}
}
13 changes: 13 additions & 0 deletions src/interfaces/IArbSys.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/**
* @notice Minimal interface for interacting with Arbitrum system contracts
*/
interface IArbSys {
/**
* @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0)
* @return block number as int
*/
function arbBlockNumber() external view returns (uint256);
}
6 changes: 4 additions & 2 deletions src/lib/ExclusivityLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.0;

import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol";
import {ResolvedOrder, OutputToken} from "../base/ReactorStructs.sol";
import {IArbSys} from "../interfaces/IArbSys.sol";

/// @title ExclusiveOverride
/// @dev This library handles order exclusivity
Expand Down Expand Up @@ -40,9 +41,10 @@ library ExclusivityLib {
ResolvedOrder memory order,
address exclusive,
uint256 exclusivityEnd,
uint256 exclusivityOverrideBps
uint256 exclusivityOverrideBps,
uint256 blockNumberish
) internal view {
_handleExclusiveOverride(order, exclusive, exclusivityEnd, exclusivityOverrideBps, block.number);
_handleExclusiveOverride(order, exclusive, exclusivityEnd, exclusivityOverrideBps, blockNumberish);
}

/// @notice Applies exclusivity override to the resolved order if necessary
Expand Down
81 changes: 53 additions & 28 deletions src/lib/NonlinearDutchDecayLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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";

/// @notice helpers for handling non-linear dutch order objects
library NonlinearDutchDecayLib {
Expand All @@ -18,41 +19,47 @@ library NonlinearDutchDecayLib {
/// @notice thrown when the decay curve is invalid
error InvalidDecayCurve();

/// @notice Calculates the decayed amount based on the current block and the defined curve
/// @notice Struct to hold decay parameters
/// @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
/// @param decayFunc The decay function to use
/// @dev Expects the relativeBlocks in curve to be strictly increasing
struct DecayParams {
NonlinearDutchDecay curve;
uint256 startAmount;
uint256 decayStartBlock;
uint256 blockNumberish;
uint256 minAmount;
uint256 maxAmount;
function(uint256, uint256, uint256, int256, int256) internal pure returns (int256) decayFunc;
}

/// @notice Calculates the decayed amount based on the current block and the defined curve
/// @param params The decay parameters
/// @return decayedAmount The decayed amount
function decay(
NonlinearDutchDecay memory curve,
uint256 startAmount,
uint256 decayStartBlock,
uint256 minAmount,
uint256 maxAmount,
function(uint256, uint256, uint256, int256, int256) internal pure returns (int256) decayFunc
) internal view returns (uint256 decayedAmount) {
function decay(DecayParams memory params) internal pure returns (uint256 decayedAmount) {
// mismatch of relativeAmounts and relativeBlocks
if (curve.relativeAmounts.length > 16) {
if (params.curve.relativeAmounts.length > 16) {
revert InvalidDecayCurve();
}

// handle current block before decay or no decay
if (decayStartBlock >= block.number || curve.relativeAmounts.length == 0) {
return startAmount.bound(minAmount, maxAmount);
if (params.decayStartBlock >= params.blockNumberish || params.curve.relativeAmounts.length == 0) {
return params.startAmount.bound(params.minAmount, params.maxAmount);
}
// 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 blockDelta = uint16(Math.min(params.blockNumberish - params.decayStartBlock, type(uint16).max));
(uint16 startPoint, uint16 endPoint, int256 relStartAmount, int256 relEndAmount) =
locateCurvePosition(curve, blockDelta);
locateCurvePosition(params.curve, blockDelta);
// get decay of only the relative amounts
int256 curveDelta = decayFunc(startPoint, endPoint, blockDelta, relStartAmount, relEndAmount);
int256 curveDelta = params.decayFunc(startPoint, endPoint, blockDelta, relStartAmount, relEndAmount);

return startAmount.boundedSub(curveDelta, minAmount, maxAmount);
return params.startAmount.boundedSub(curveDelta, params.minAmount, params.maxAmount);
}

/// @notice Locates the current position on the decay curve based on the elapsed blocks
Expand Down Expand Up @@ -98,45 +105,63 @@ library NonlinearDutchDecayLib {
/// @notice returns a decayed output using the given dutch spec and blocks
/// @param output The output to decay
/// @param decayStartBlock The block to start decaying
/// @param blockNumberish The block number to decay to
/// @return result a decayed output
function decay(V3DutchOutput memory output, uint256 decayStartBlock)
function decay(V3DutchOutput memory output, uint256 decayStartBlock, uint256 blockNumberish)
internal
view
pure
returns (OutputToken memory result)
{
uint256 decayedOutput = decay(
output.curve, output.startAmount, decayStartBlock, output.minAmount, type(uint256).max, v3LinearOutputDecay
);
DecayParams memory params = DecayParams({
curve: output.curve,
startAmount: output.startAmount,
decayStartBlock: decayStartBlock,
blockNumberish: blockNumberish,
minAmount: output.minAmount,
maxAmount: type(uint256).max,
decayFunc: v3LinearOutputDecay
});
uint256 decayedOutput = decay(params);
result = OutputToken(output.token, decayedOutput, output.recipient);
}

/// @notice returns a decayed output array using the given dutch spec and blocks
/// @param outputs The output array to decay
/// @param decayStartBlock The block to start decaying
/// @param blockNumberish The block number to decay to
/// @return result a decayed output array
function decay(V3DutchOutput[] memory outputs, uint256 decayStartBlock)
function decay(V3DutchOutput[] memory outputs, uint256 decayStartBlock, uint256 blockNumberish)
internal
view
pure
returns (OutputToken[] memory result)
{
uint256 outputLength = outputs.length;
result = new OutputToken[](outputLength);
for (uint256 i = 0; i < outputLength; i++) {
result[i] = decay(outputs[i], decayStartBlock);
result[i] = decay(outputs[i], decayStartBlock, blockNumberish);
}
}

/// @notice returns a decayed input using the given dutch spec and times
/// @param input The input to decay
/// @param decayStartBlock The block to start decaying
/// @param blockNumberish The block number to decay to
/// @return result a decayed input
function decay(V3DutchInput memory input, uint256 decayStartBlock)
function decay(V3DutchInput memory input, uint256 decayStartBlock, uint256 blockNumberish)
internal
view
pure
returns (InputToken memory result)
{
uint256 decayedInput =
decay(input.curve, input.startAmount, decayStartBlock, 0, input.maxAmount, v3LinearInputDecay);
DecayParams memory params = DecayParams({
curve: input.curve,
startAmount: input.startAmount,
decayStartBlock: decayStartBlock,
blockNumberish: blockNumberish,
minAmount: 0,
maxAmount: input.maxAmount,
decayFunc: v3LinearInputDecay
});
uint256 decayedInput = decay(params);
result = InputToken(input.token, decayedInput, input.maxAmount);
}

Expand Down
17 changes: 12 additions & 5 deletions src/reactors/V3DutchOrderReactor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {ExclusivityLib} from "../lib/ExclusivityLib.sol";
import {NonlinearDutchDecayLib} from "../lib/NonlinearDutchDecayLib.sol";
import {V3DutchOrderLib, V3DutchOrder, CosignerData, V3DutchOutput, V3DutchInput} from "../lib/V3DutchOrderLib.sol";
import {SignedOrder, ResolvedOrder} from "../base/ReactorStructs.sol";
import {BlockNumberish} from "../base/BlockNumberish.sol";
import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol";
import {MathExt} from "../lib/MathExt.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
Expand All @@ -22,7 +23,7 @@ import {CosignerLib} from "../lib/CosignerLib.sol";
/// - For each outputAmount:
/// - If amount is 0, then use baseOutput
/// - If amount is nonzero, then ensure it is greater than specified baseOutput and replace startAmount
contract V3DutchOrderReactor is BaseReactor {
contract V3DutchOrderReactor is BaseReactor, BlockNumberish {
using Permit2Lib for ResolvedOrder;
using V3DutchOrderLib for V3DutchOrder;
using NonlinearDutchDecayLib for V3DutchOutput[];
Expand All @@ -40,7 +41,10 @@ contract V3DutchOrderReactor is BaseReactor {
/// @notice thrown when an order's cosigner output is less than the specified
error InvalidCosignerOutput();

constructor(IPermit2 _permit2, address _protocolFeeOwner) BaseReactor(_permit2, _protocolFeeOwner) {}
constructor(IPermit2 _permit2, address _protocolFeeOwner)
BaseReactor(_permit2, _protocolFeeOwner)
BlockNumberish()
{}

/// @inheritdoc BaseReactor
function _resolve(SignedOrder calldata signedOrder)
Expand All @@ -58,17 +62,20 @@ contract V3DutchOrderReactor is BaseReactor {
_updateWithCosignerAmounts(order);
_updateWithGasAdjustment(order);

uint256 blockNumberish = _getBlockNumberish();

resolvedOrder = ResolvedOrder({
info: order.info,
input: order.baseInput.decay(order.cosignerData.decayStartBlock),
outputs: order.baseOutputs.decay(order.cosignerData.decayStartBlock),
input: order.baseInput.decay(order.cosignerData.decayStartBlock, blockNumberish),
outputs: order.baseOutputs.decay(order.cosignerData.decayStartBlock, blockNumberish),
sig: signedOrder.sig,
hash: orderHash
});
resolvedOrder.handleExclusiveOverrideBlock(
order.cosignerData.exclusiveFiller,
order.cosignerData.decayStartBlock,
order.cosignerData.exclusivityOverrideBps
order.cosignerData.exclusivityOverrideBps,
blockNumberish
);
}

Expand Down
Loading

0 comments on commit c145fa4

Please sign in to comment.