Skip to content

Commit a40017f

Browse files
committed
Revert "improve(SpokePool): _depositV3 interprets exclusivityParameter as an offset, or a timestamp (#670)"
This reverts commit fac5dd1.
1 parent c0dab5d commit a40017f

File tree

6 files changed

+92
-301
lines changed

6 files changed

+92
-301
lines changed

contracts/SpokePool.sol

Lines changed: 62 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -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

contracts/interfaces/V3SpokePoolInterface.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ interface V3SpokePoolInterface {
222222
error DisabledRoute();
223223
error InvalidQuoteTimestamp();
224224
error InvalidFillDeadline();
225-
error InvalidExclusiveRelayer();
225+
error InvalidExclusivityDeadline();
226226
error MsgValueDoesNotMatchInputAmount();
227227
error NotExclusiveRelayer();
228228
error NoSlowFillsInExclusivityWindow();

0 commit comments

Comments
 (0)