Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

audit(14): arbitrum block number #280

Merged
merged 12 commits into from
Oct 18, 2024
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() {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
// 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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add this as a submodule:

[submodule "lib/nitro-contracts"]
	path = lib/nitro-contracts
	url = https://github.com/OffchainLabs/nitro-contracts

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree but not sure if necessary since we also don't need the entire IArbSys interface, just this one func

/**
* @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
Loading