@@ -144,10 +144,6 @@ abstract contract SpokePool is
144144 // token for the input token. By using this magic value, off-chain validators do not have to keep
145145 // this event in their lookback window when querying for expired deposts.
146146 uint32 public constant INFINITE_FILL_DEADLINE = type (uint32 ).max;
147-
148- // One year in seconds. If `exclusivityParameter` is set to a value less than this, then the emitted
149- // exclusivityDeadline in a deposit event will be set to the current time plus this value.
150- uint32 public constant MAX_EXCLUSIVITY_PERIOD_SECONDS = 31_536_000 ;
151147 /****************************************
152148 * EVENTS *
153149 ****************************************/
@@ -433,8 +429,9 @@ abstract contract SpokePool is
433429 /**
434430 * @notice Previously, this function allowed the caller to specify the exclusivityDeadline, otherwise known as the
435431 * as exact timestamp on the destination chain before which only the exclusiveRelayer could fill the deposit. Now,
436- * the caller is expected to pass in a number that will be interpreted either as an offset or a fixed
437- * timestamp depending on its value.
432+ * the caller is expected to pass in an exclusivityPeriod which is the number of seconds to be added to the
433+ * block.timestamp to produce the exclusivityDeadline. This allows the caller to ignore any latency associated
434+ * with this transaction being mined and propagating this transaction to the miner.
438435 * @notice Request to bridge input token cross chain to a destination chain and receive a specified amount
439436 * of output tokens. The fee paid to relayers and the system should be captured in the spread between output
440437 * amount and input amount when adjusted to be denominated in the input token. A relayer on the destination
@@ -473,17 +470,9 @@ abstract contract SpokePool is
473470 * @param fillDeadline The deadline for the relayer to fill the deposit. After this destination chain timestamp,
474471 * the fill will revert on the destination chain. Must be set between [currentTime, currentTime + fillDeadlineBuffer]
475472 * where currentTime is block.timestamp on this chain or this transaction will revert.
476- * @param exclusivityParameter This value is used to set the exclusivity deadline timestamp in the emitted deposit
477- * event. Before this destinationchain timestamp, only the exclusiveRelayer (if set to a non-zero address),
478- * can fill this deposit. There are three ways to use this parameter:
479- * 1. NO EXCLUSIVITY: If this value is set to 0, then a timestamp of 0 will be emitted,
480- * meaning that there is no exclusivity period.
481- * 2. OFFSET: If this value is less than MAX_EXCLUSIVITY_PERIOD_SECONDS, then add this value to
482- * the block.timestamp to derive the exclusive relayer deadline. Note that using the parameter in this way
483- * will expose the filler of the deposit to the risk that the block.timestamp of this event gets changed
484- * due to a chain-reorg, which would also change the exclusivity timestamp.
485- * 3. TIMESTAMP: Otherwise, set this value as the exclusivity deadline timestamp.
486- * which is the deadline for the exclusiveRelayer to fill the deposit.
473+ * @param exclusivityPeriod Added to the current time to set the exclusive relayer deadline,
474+ * which is the deadline for the exclusiveRelayer to fill the deposit. After this destination chain timestamp,
475+ * anyone can fill the deposit.
487476 * @param message The message to send to the recipient on the destination chain if the recipient is a contract.
488477 * If the message is not empty, the recipient contract must implement handleV3AcrossMessage() or the fill will revert.
489478 */
@@ -498,23 +487,62 @@ abstract contract SpokePool is
498487 address exclusiveRelayer ,
499488 uint32 quoteTimestamp ,
500489 uint32 fillDeadline ,
501- uint32 exclusivityParameter ,
490+ uint32 exclusivityPeriod ,
502491 bytes calldata message
503492 ) public payable override nonReentrant unpausedDeposits {
504- _depositV3 (
505- depositor,
506- recipient,
493+ // Check that deposit route is enabled for the input token. There are no checks required for the output token
494+ // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
495+ if (! enabledDepositRoutes[inputToken][destinationChainId]) revert DisabledRoute ();
496+
497+ // Require that quoteTimestamp has a maximum age so that depositors pay an LP fee based on recent HubPool usage.
498+ // It is assumed that cross-chain timestamps are normally loosely in-sync, but clock drift can occur. If the
499+ // SpokePool time stalls or lags significantly, it is still possible to make deposits by setting quoteTimestamp
500+ // within the configured buffer. The owner should pause deposits/fills if this is undesirable.
501+ // This will underflow if quoteTimestamp is more than depositQuoteTimeBuffer;
502+ // this is safe but will throw an unintuitive error.
503+
504+ // slither-disable-next-line timestamp
505+ uint256 currentTime = getCurrentTime ();
506+ if (currentTime - quoteTimestamp > depositQuoteTimeBuffer) revert InvalidQuoteTimestamp ();
507+
508+ // fillDeadline is relative to the destination chain.
509+ // Don’t allow fillDeadline to be more than several bundles into the future.
510+ // This limits the maximum required lookback for dataworker and relayer instances.
511+ // Also, don't allow fillDeadline to be in the past. This poses a potential UX issue if the destination
512+ // chain time keeping and this chain's time keeping are out of sync but is not really a practical hurdle
513+ // unless they are significantly out of sync or the depositor is setting very short fill deadlines. This latter
514+ // situation won't be a problem for honest users.
515+ if (fillDeadline < currentTime || fillDeadline > currentTime + fillDeadlineBuffer) revert InvalidFillDeadline ();
516+
517+ // If the address of the origin token is a wrappedNativeToken contract and there is a msg.value with the
518+ // transaction then the user is sending the native token. In this case, the native token should be
519+ // wrapped.
520+ if (inputToken == address (wrappedNativeToken) && msg .value > 0 ) {
521+ if (msg .value != inputAmount) revert MsgValueDoesNotMatchInputAmount ();
522+ wrappedNativeToken.deposit { value: msg .value }();
523+ // Else, it is a normal ERC20. In this case pull the token from the caller as per normal.
524+ // Note: this includes the case where the L2 caller has WETH (already wrapped ETH) and wants to bridge them.
525+ // In this case the msg.value will be set to 0, indicating a "normal" ERC20 bridging action.
526+ } else {
527+ // msg.value should be 0 if input token isn't the wrapped native token.
528+ if (msg .value != 0 ) revert MsgValueDoesNotMatchInputAmount ();
529+ IERC20Upgradeable (inputToken).safeTransferFrom (msg .sender , address (this ), inputAmount);
530+ }
531+
532+ emit V3FundsDeposited (
507533 inputToken,
508534 outputToken,
509535 inputAmount,
510536 outputAmount,
511537 destinationChainId,
512- exclusiveRelayer,
513538 // Increment count of deposits so that deposit ID for this spoke pool is unique.
514539 numberOfDeposits++ ,
515540 quoteTimestamp,
516541 fillDeadline,
517- exclusivityParameter,
542+ uint32 (currentTime) + exclusivityPeriod,
543+ depositor,
544+ recipient,
545+ exclusiveRelayer,
518546 message
519547 );
520548 }
@@ -735,9 +763,7 @@ abstract contract SpokePool is
735763 * - fillDeadline The deadline for the caller to fill the deposit. After this timestamp,
736764 * the fill will revert on the destination chain.
737765 * - exclusivityDeadline: The deadline for the exclusive relayer to fill the deposit. After this
738- * timestamp, anyone can fill this deposit. Note that if this value was set in depositV3 by adding an offset
739- * to the deposit's block.timestamp, there is re-org risk for the caller of this method because the event's
740- * block.timestamp can change. Read the comments in `depositV3` about the `exclusivityParameter` for more details.
766+ * timestamp, anyone can fill this deposit.
741767 * - message The message to send to the recipient if the recipient is a contract that implements a
742768 * handleV3AcrossMessage() public function
743769 * @param repaymentChainId Chain of SpokePool where relayer wants to be refunded after the challenge window has
@@ -752,7 +778,7 @@ abstract contract SpokePool is
752778 // Exclusivity deadline is inclusive and is the latest timestamp that the exclusive relayer has sole right
753779 // to fill the relay.
754780 if (
755- _fillIsExclusive (relayData.exclusivityDeadline, uint32 (getCurrentTime ())) &&
781+ _fillIsExclusive (relayData.exclusiveRelayer, relayData. exclusivityDeadline, uint32 (getCurrentTime ())) &&
756782 relayData.exclusiveRelayer != msg .sender
757783 ) {
758784 revert NotExclusiveRelayer ();
@@ -798,7 +824,7 @@ abstract contract SpokePool is
798824 // Exclusivity deadline is inclusive and is the latest timestamp that the exclusive relayer has sole right
799825 // to fill the relay.
800826 if (
801- _fillIsExclusive (relayData.exclusivityDeadline, uint32 (getCurrentTime ())) &&
827+ _fillIsExclusive (relayData.exclusiveRelayer, relayData. exclusivityDeadline, uint32 (getCurrentTime ())) &&
802828 relayData.exclusiveRelayer != msg .sender
803829 ) {
804830 revert NotExclusiveRelayer ();
@@ -847,7 +873,7 @@ abstract contract SpokePool is
847873 // fast fill within this deadline. Moreover, the depositor should expect to get *fast* filled within
848874 // this deadline, not slow filled. As a simplifying assumption, we will not allow slow fills to be requested
849875 // during this exclusivity period.
850- if (_fillIsExclusive (relayData.exclusivityDeadline, currentTime)) {
876+ if (_fillIsExclusive (relayData.exclusiveRelayer, relayData. exclusivityDeadline, currentTime)) {
851877 revert NoSlowFillsInExclusivityWindow ();
852878 }
853879 if (relayData.fillDeadline < currentTime) revert ExpiredFillDeadline ();
@@ -1022,99 +1048,6 @@ abstract contract SpokePool is
10221048 * INTERNAL FUNCTIONS *
10231049 **************************************/
10241050
1025- function _depositV3 (
1026- address depositor ,
1027- address recipient ,
1028- address inputToken ,
1029- address outputToken ,
1030- uint256 inputAmount ,
1031- uint256 outputAmount ,
1032- uint256 destinationChainId ,
1033- address exclusiveRelayer ,
1034- uint32 depositId ,
1035- uint32 quoteTimestamp ,
1036- uint32 fillDeadline ,
1037- uint32 exclusivityParameter ,
1038- bytes calldata message
1039- ) internal {
1040- // Check that deposit route is enabled for the input token. There are no checks required for the output token
1041- // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
1042- if (! enabledDepositRoutes[inputToken][destinationChainId]) revert DisabledRoute ();
1043-
1044- // Require that quoteTimestamp has a maximum age so that depositors pay an LP fee based on recent HubPool usage.
1045- // It is assumed that cross-chain timestamps are normally loosely in-sync, but clock drift can occur. If the
1046- // SpokePool time stalls or lags significantly, it is still possible to make deposits by setting quoteTimestamp
1047- // within the configured buffer. The owner should pause deposits/fills if this is undesirable.
1048- // This will underflow if quoteTimestamp is more than depositQuoteTimeBuffer;
1049- // this is safe but will throw an unintuitive error.
1050-
1051- // slither-disable-next-line timestamp
1052- uint256 currentTime = getCurrentTime ();
1053- if (currentTime - quoteTimestamp > depositQuoteTimeBuffer) revert InvalidQuoteTimestamp ();
1054-
1055- // fillDeadline is relative to the destination chain.
1056- // Don’t allow fillDeadline to be more than several bundles into the future.
1057- // This limits the maximum required lookback for dataworker and relayer instances.
1058- // Also, don't allow fillDeadline to be in the past. This poses a potential UX issue if the destination
1059- // chain time keeping and this chain's time keeping are out of sync but is not really a practical hurdle
1060- // unless they are significantly out of sync or the depositor is setting very short fill deadlines. This latter
1061- // situation won't be a problem for honest users.
1062- if (fillDeadline < currentTime || fillDeadline > currentTime + fillDeadlineBuffer) revert InvalidFillDeadline ();
1063-
1064- // There are three cases for setting the exclusivity deadline using the exclusivity parameter:
1065- // 1. If this parameter is 0, then there is no exclusivity period and emit 0 for the deadline. This
1066- // means that fillers of this deposit do not have to worry about the block.timestamp of this event changing
1067- // due to re-orgs when filling this deposit.
1068- // 2. If the exclusivity parameter is less than or equal to MAX_EXCLUSIVITY_PERIOD_SECONDS, then the exclusivity
1069- // deadline is set to the block.timestamp of this event plus the exclusivity parameter. This means that the
1070- // filler of this deposit assumes re-org risk when filling this deposit because the block.timestamp of this
1071- // event affects the exclusivity deadline.
1072- // 3. Otherwise, interpret this parameter as a timestamp and emit it as the exclusivity deadline. This means
1073- // that the filler of this deposit will not assume re-org risk related to the block.timestamp of this
1074- // event changing.
1075- uint32 exclusivityDeadline = exclusivityParameter;
1076- if (exclusivityDeadline > 0 ) {
1077- if (exclusivityDeadline <= MAX_EXCLUSIVITY_PERIOD_SECONDS) {
1078- exclusivityDeadline += uint32 (currentTime);
1079- }
1080-
1081- // As a safety measure, prevent caller from inadvertently locking funds during exclusivity period
1082- // by forcing them to specify an exclusive relayer.
1083- if (exclusiveRelayer == address (0 )) revert InvalidExclusiveRelayer ();
1084- }
1085-
1086- // If the address of the origin token is a wrappedNativeToken contract and there is a msg.value with the
1087- // transaction then the user is sending the native token. In this case, the native token should be
1088- // wrapped.
1089- if (inputToken == address (wrappedNativeToken) && msg .value > 0 ) {
1090- if (msg .value != inputAmount) revert MsgValueDoesNotMatchInputAmount ();
1091- wrappedNativeToken.deposit { value: msg .value }();
1092- // Else, it is a normal ERC20. In this case pull the token from the caller as per normal.
1093- // Note: this includes the case where the L2 caller has WETH (already wrapped ETH) and wants to bridge them.
1094- // In this case the msg.value will be set to 0, indicating a "normal" ERC20 bridging action.
1095- } else {
1096- // msg.value should be 0 if input token isn't the wrapped native token.
1097- if (msg .value != 0 ) revert MsgValueDoesNotMatchInputAmount ();
1098- IERC20Upgradeable (inputToken).safeTransferFrom (msg .sender , address (this ), inputAmount);
1099- }
1100-
1101- emit V3FundsDeposited (
1102- inputToken,
1103- outputToken,
1104- inputAmount,
1105- outputAmount,
1106- destinationChainId,
1107- depositId,
1108- quoteTimestamp,
1109- fillDeadline,
1110- exclusivityDeadline,
1111- depositor,
1112- recipient,
1113- exclusiveRelayer,
1114- message
1115- );
1116- }
1117-
11181051 function _deposit (
11191052 address depositor ,
11201053 address recipient ,
@@ -1426,9 +1359,13 @@ abstract contract SpokePool is
14261359 }
14271360 }
14281361
1429- // Determine whether the exclusivityDeadline implies active exclusivity.
1430- function _fillIsExclusive (uint32 exclusivityDeadline , uint32 currentTime ) internal pure returns (bool ) {
1431- return exclusivityDeadline >= currentTime;
1362+ // Determine whether the combination of exlcusiveRelayer and exclusivityDeadline implies active exclusivity.
1363+ function _fillIsExclusive (
1364+ address exclusiveRelayer ,
1365+ uint32 exclusivityDeadline ,
1366+ uint32 currentTime
1367+ ) internal pure returns (bool ) {
1368+ return exclusivityDeadline >= currentTime && exclusiveRelayer != address (0 );
14321369 }
14331370
14341371 // Implementing contract needs to override this to ensure that only the appropriate cross chain admin can execute
0 commit comments