Skip to content

Commit d363bf0

Browse files
committed
Revert "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)"
This reverts commit 9d21d1b.
1 parent 108be77 commit d363bf0

File tree

4 files changed

+52
-234
lines changed

4 files changed

+52
-234
lines changed

contracts/SpokePool.sol

Lines changed: 44 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ abstract contract SpokePool is
125125

126126
bytes32 public constant UPDATE_V3_DEPOSIT_DETAILS_HASH =
127127
keccak256(
128-
"UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
128+
"UpdateDepositDetails(uint32 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
129129
);
130130

131131
// Default chain Id used to signify that no repayment is requested, for example when executing a slow fill.
@@ -549,92 +549,59 @@ abstract contract SpokePool is
549549
uint32 exclusivityPeriod,
550550
bytes calldata message
551551
) public payable override nonReentrant unpausedDeposits {
552-
_depositV3(
553-
depositor,
554-
recipient,
552+
// Check that deposit route is enabled for the input token. There are no checks required for the output token
553+
// which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
554+
if (!enabledDepositRoutes[inputToken][destinationChainId]) revert DisabledRoute();
555+
556+
// Require that quoteTimestamp has a maximum age so that depositors pay an LP fee based on recent HubPool usage.
557+
// It is assumed that cross-chain timestamps are normally loosely in-sync, but clock drift can occur. If the
558+
// SpokePool time stalls or lags significantly, it is still possible to make deposits by setting quoteTimestamp
559+
// within the configured buffer. The owner should pause deposits/fills if this is undesirable.
560+
// This will underflow if quoteTimestamp is more than depositQuoteTimeBuffer;
561+
// this is safe but will throw an unintuitive error.
562+
563+
// slither-disable-next-line timestamp
564+
uint256 currentTime = getCurrentTime();
565+
if (currentTime - quoteTimestamp > depositQuoteTimeBuffer) revert InvalidQuoteTimestamp();
566+
567+
// fillDeadline is relative to the destination chain.
568+
// Don’t allow fillDeadline to be more than several bundles into the future.
569+
// This limits the maximum required lookback for dataworker and relayer instances.
570+
// Also, don't allow fillDeadline to be in the past. This poses a potential UX issue if the destination
571+
// chain time keeping and this chain's time keeping are out of sync but is not really a practical hurdle
572+
// unless they are significantly out of sync or the depositor is setting very short fill deadlines. This latter
573+
// situation won't be a problem for honest users.
574+
if (fillDeadline < currentTime || fillDeadline > currentTime + fillDeadlineBuffer) revert InvalidFillDeadline();
575+
576+
// If the address of the origin token is a wrappedNativeToken contract and there is a msg.value with the
577+
// transaction then the user is sending the native token. In this case, the native token should be
578+
// wrapped.
579+
if (inputToken == address(wrappedNativeToken) && msg.value > 0) {
580+
if (msg.value != inputAmount) revert MsgValueDoesNotMatchInputAmount();
581+
wrappedNativeToken.deposit{ value: msg.value }();
582+
// Else, it is a normal ERC20. In this case pull the token from the caller as per normal.
583+
// Note: this includes the case where the L2 caller has WETH (already wrapped ETH) and wants to bridge them.
584+
// In this case the msg.value will be set to 0, indicating a "normal" ERC20 bridging action.
585+
} else {
586+
// msg.value should be 0 if input token isn't the wrapped native token.
587+
if (msg.value != 0) revert MsgValueDoesNotMatchInputAmount();
588+
IERC20Upgradeable(inputToken).safeTransferFrom(msg.sender, address(this), inputAmount);
589+
}
590+
591+
emit V3FundsDeposited(
555592
inputToken,
556593
outputToken,
557594
inputAmount,
558595
outputAmount,
559596
destinationChainId,
560-
exclusiveRelayer,
561597
// Increment count of deposits so that deposit ID for this spoke pool is unique.
562-
// @dev Implicitly casts from uint32 to uint256 by padding the left-most bytes with zeros. Guarantees
563-
// that the 24 most significant bytes are 0.
564598
numberOfDeposits++,
565599
quoteTimestamp,
566600
fillDeadline,
567-
uint32(getCurrentTime()) + exclusivityPeriod,
568-
message
569-
);
570-
}
571-
572-
/**
573-
* @notice See depositV3 for details. This function is identical to depositV3 except that it does not use the
574-
* global deposit ID counter as a deposit nonce, instead allowing the caller to pass in a deposit nonce. This
575-
* function is designed to be used by anyone who wants to pre-compute their resultant relay data hash, which
576-
* could be useful for filling a deposit faster and avoiding any risk of a relay hash unexpectedly changing
577-
* due to another deposit front-running this one and incrementing the global deposit ID counter.
578-
* @dev This is labeled "unsafe" because there is no guarantee that the depositId emitted in the resultant
579-
* V3FundsDeposited event is unique which means that the
580-
* corresponding fill might collide with an existing relay hash on the destination chain SpokePool,
581-
* which would make this deposit unfillable. In this case, the depositor would subsequently receive a refund
582-
* of `inputAmount` of `inputToken` on the origin chain after the fill deadline.
583-
* @dev On the destination chain, the hash of the deposit data will be used to uniquely identify this deposit, so
584-
* modifying any params in it will result in a different hash and a different deposit. The hash will comprise
585-
* all parameters to this function along with this chain's chainId(). Relayers are only refunded for filling
586-
* deposits with deposit hashes that map exactly to the one emitted by this contract.
587-
* @param depositNonce The nonce that uniquely identifies this deposit. This function will combine this parameter
588-
* with the msg.sender address to create a unique uint256 depositNonce and ensure that the msg.sender cannot
589-
* use this function to front-run another depositor's unsafe deposit. This function guarantees that the resultant
590-
* deposit nonce will not collide with a safe uint256 deposit nonce whose 24 most significant bytes are always 0.
591-
* @param depositor See identically named parameter in depositV3() comments.
592-
* @param recipient See identically named parameter in depositV3() comments.
593-
* @param inputToken See identically named parameter in depositV3() comments.
594-
* @param outputToken See identically named parameter in depositV3() comments.
595-
* @param inputAmount See identically named parameter in depositV3() comments.
596-
* @param outputAmount See identically named parameter in depositV3() comments.
597-
* @param destinationChainId See identically named parameter in depositV3() comments.
598-
* @param exclusiveRelayer See identically named parameter in depositV3() comments.
599-
* @param quoteTimestamp See identically named parameter in depositV3() comments.
600-
* @param fillDeadline See identically named parameter in depositV3() comments.
601-
* @param exclusivityPeriod See identically named parameter in depositV3() comments.
602-
* @param message See identically named parameter in depositV3() comments.
603-
*/
604-
function unsafeDepositV3(
605-
address depositor,
606-
address recipient,
607-
address inputToken,
608-
address outputToken,
609-
uint256 inputAmount,
610-
uint256 outputAmount,
611-
uint256 destinationChainId,
612-
address exclusiveRelayer,
613-
uint256 depositNonce,
614-
uint32 quoteTimestamp,
615-
uint32 fillDeadline,
616-
uint32 exclusivityPeriod,
617-
bytes calldata message
618-
) public payable nonReentrant unpausedDeposits {
619-
// @dev Create the uint256 deposit ID by concatenating the msg.sender and depositor address with the inputted
620-
// depositNonce parameter. The resultant 32 byte string will be hashed and then casted to an "unsafe"
621-
// uint256 deposit ID. The probability that the resultant ID collides with a "safe" deposit ID is
622-
// equal to the chance that the first 28 bytes of the hash are 0, which is too small for us to consider.
623-
624-
uint256 depositId = getUnsafeDepositId(msg.sender, depositor, depositNonce);
625-
_depositV3(
601+
uint32(currentTime) + exclusivityPeriod,
626602
depositor,
627603
recipient,
628-
inputToken,
629-
outputToken,
630-
inputAmount,
631-
outputAmount,
632-
destinationChainId,
633604
exclusiveRelayer,
634-
depositId,
635-
quoteTimestamp,
636-
fillDeadline,
637-
uint32(getCurrentTime()) + exclusivityPeriod,
638605
message
639606
);
640607
}
@@ -788,7 +755,7 @@ abstract contract SpokePool is
788755
*/
789756
function speedUpV3Deposit(
790757
address depositor,
791-
uint256 depositId,
758+
uint32 depositId,
792759
uint256 updatedOutputAmount,
793760
address updatedRecipient,
794761
bytes calldata updatedMessage,
@@ -1117,99 +1084,10 @@ abstract contract SpokePool is
11171084
return block.timestamp; // solhint-disable-line not-rely-on-time
11181085
}
11191086

1120-
/**
1121-
* @notice Returns the deposit ID for an unsafe deposit. This function is used to compute the deposit ID
1122-
* in unsafeDepositV3 and is provided as a convenience.
1123-
* @dev msgSenderand depositor are both used as inputs to allow passthrough depositors to create unique
1124-
* deposit hash spaces for unique depositors.
1125-
* @param msgSender The caller of the transaction used as input to produce the deposit ID.
1126-
* @param depositor The depositor address used as input to produce the deposit ID.
1127-
* @param depositNonce The nonce used as input to produce the deposit ID.
1128-
* @return The deposit ID for the unsafe deposit.
1129-
*/
1130-
function getUnsafeDepositId(
1131-
address msgSender,
1132-
address depositor,
1133-
uint256 depositNonce
1134-
) public pure returns (uint256) {
1135-
return uint256(keccak256(abi.encodePacked(msgSender, depositor, depositNonce)));
1136-
}
1137-
11381087
/**************************************
11391088
* INTERNAL FUNCTIONS *
11401089
**************************************/
11411090

1142-
function _depositV3(
1143-
address depositor,
1144-
address recipient,
1145-
address inputToken,
1146-
address outputToken,
1147-
uint256 inputAmount,
1148-
uint256 outputAmount,
1149-
uint256 destinationChainId,
1150-
address exclusiveRelayer,
1151-
uint256 depositId,
1152-
uint32 quoteTimestamp,
1153-
uint32 fillDeadline,
1154-
uint32 exclusivityDeadline,
1155-
bytes calldata message
1156-
) internal {
1157-
// Check that deposit route is enabled for the input token. There are no checks required for the output token
1158-
// which is pulled from the relayer at fill time and passed through this contract atomically to the recipient.
1159-
if (!enabledDepositRoutes[inputToken][destinationChainId]) revert DisabledRoute();
1160-
1161-
// Require that quoteTimestamp has a maximum age so that depositors pay an LP fee based on recent HubPool usage.
1162-
// It is assumed that cross-chain timestamps are normally loosely in-sync, but clock drift can occur. If the
1163-
// SpokePool time stalls or lags significantly, it is still possible to make deposits by setting quoteTimestamp
1164-
// within the configured buffer. The owner should pause deposits/fills if this is undesirable.
1165-
// This will underflow if quoteTimestamp is more than depositQuoteTimeBuffer;
1166-
// this is safe but will throw an unintuitive error.
1167-
1168-
// slither-disable-next-line timestamp
1169-
uint256 currentTime = getCurrentTime();
1170-
if (currentTime - quoteTimestamp > depositQuoteTimeBuffer) revert InvalidQuoteTimestamp();
1171-
1172-
// fillDeadline is relative to the destination chain.
1173-
// Don’t allow fillDeadline to be more than several bundles into the future.
1174-
// This limits the maximum required lookback for dataworker and relayer instances.
1175-
// Also, don't allow fillDeadline to be in the past. This poses a potential UX issue if the destination
1176-
// chain time keeping and this chain's time keeping are out of sync but is not really a practical hurdle
1177-
// unless they are significantly out of sync or the depositor is setting very short fill deadlines. This latter
1178-
// situation won't be a problem for honest users.
1179-
if (fillDeadline < currentTime || fillDeadline > currentTime + fillDeadlineBuffer) revert InvalidFillDeadline();
1180-
1181-
// If the address of the origin token is a wrappedNativeToken contract and there is a msg.value with the
1182-
// transaction then the user is sending the native token. In this case, the native token should be
1183-
// wrapped.
1184-
if (inputToken == address(wrappedNativeToken) && msg.value > 0) {
1185-
if (msg.value != inputAmount) revert MsgValueDoesNotMatchInputAmount();
1186-
wrappedNativeToken.deposit{ value: msg.value }();
1187-
// Else, it is a normal ERC20. In this case pull the token from the caller as per normal.
1188-
// Note: this includes the case where the L2 caller has WETH (already wrapped ETH) and wants to bridge them.
1189-
// In this case the msg.value will be set to 0, indicating a "normal" ERC20 bridging action.
1190-
} else {
1191-
// msg.value should be 0 if input token isn't the wrapped native token.
1192-
if (msg.value != 0) revert MsgValueDoesNotMatchInputAmount();
1193-
IERC20Upgradeable(inputToken).safeTransferFrom(msg.sender, address(this), inputAmount);
1194-
}
1195-
1196-
emit V3FundsDeposited(
1197-
inputToken,
1198-
outputToken,
1199-
inputAmount,
1200-
outputAmount,
1201-
destinationChainId,
1202-
depositId,
1203-
quoteTimestamp,
1204-
fillDeadline,
1205-
exclusivityDeadline,
1206-
depositor,
1207-
recipient,
1208-
exclusiveRelayer,
1209-
message
1210-
);
1211-
}
1212-
12131091
function _deposit(
12141092
address depositor,
12151093
address recipient,
@@ -1336,7 +1214,7 @@ abstract contract SpokePool is
13361214

13371215
function _verifyUpdateV3DepositMessage(
13381216
address depositor,
1339-
uint256 depositId,
1217+
uint32 depositId,
13401218
uint256 originChainId,
13411219
uint256 updatedOutputAmount,
13421220
address updatedRecipient,

contracts/interfaces/V3SpokePoolInterface.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ interface V3SpokePoolInterface {
5353
// Origin chain id.
5454
uint256 originChainId;
5555
// The id uniquely identifying this deposit on the origin chain.
56-
uint256 depositId;
56+
uint32 depositId;
5757
// The timestamp on the destination chain after which this deposit can no longer be filled.
5858
uint32 fillDeadline;
5959
// The timestamp on the destination chain after which any relayer can fill the deposit.
@@ -102,7 +102,7 @@ interface V3SpokePoolInterface {
102102
uint256 inputAmount,
103103
uint256 outputAmount,
104104
uint256 indexed destinationChainId,
105-
uint256 indexed depositId,
105+
uint32 indexed depositId,
106106
uint32 quoteTimestamp,
107107
uint32 fillDeadline,
108108
uint32 exclusivityDeadline,
@@ -114,7 +114,7 @@ interface V3SpokePoolInterface {
114114

115115
event RequestedSpeedUpV3Deposit(
116116
uint256 updatedOutputAmount,
117-
uint256 indexed depositId,
117+
uint32 indexed depositId,
118118
address indexed depositor,
119119
address updatedRecipient,
120120
bytes updatedMessage,
@@ -128,7 +128,7 @@ interface V3SpokePoolInterface {
128128
uint256 outputAmount,
129129
uint256 repaymentChainId,
130130
uint256 indexed originChainId,
131-
uint256 indexed depositId,
131+
uint32 indexed depositId,
132132
uint32 fillDeadline,
133133
uint32 exclusivityDeadline,
134134
address exclusiveRelayer,
@@ -145,7 +145,7 @@ interface V3SpokePoolInterface {
145145
uint256 inputAmount,
146146
uint256 outputAmount,
147147
uint256 indexed originChainId,
148-
uint256 indexed depositId,
148+
uint32 indexed depositId,
149149
uint32 fillDeadline,
150150
uint32 exclusivityDeadline,
151151
address exclusiveRelayer,
@@ -189,7 +189,7 @@ interface V3SpokePoolInterface {
189189

190190
function speedUpV3Deposit(
191191
address depositor,
192-
uint256 depositId,
192+
uint32 depositId,
193193
uint256 updatedOutputAmount,
194194
address updatedRecipient,
195195
bytes calldata updatedMessage,

test/evm/hardhat/SpokePool.Deposit.ts

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
amountReceived,
2626
MAX_UINT32,
2727
originChainId,
28+
zeroAddress,
2829
} from "./constants";
2930

3031
const { AddressZero: ZERO_ADDRESS } = ethers.constants;
@@ -365,28 +366,6 @@ describe("SpokePool Depositor Logic", async function () {
365366
_relayData.message,
366367
];
367368
}
368-
function getUnsafeDepositArgsFromRelayData(
369-
_relayData: V3RelayData,
370-
_depositId: string,
371-
_destinationChainId = destinationChainId,
372-
_quoteTimestamp = quoteTimestamp
373-
) {
374-
return [
375-
_relayData.depositor,
376-
_relayData.recipient,
377-
_relayData.inputToken,
378-
_relayData.outputToken,
379-
_relayData.inputAmount,
380-
_relayData.outputAmount,
381-
_destinationChainId,
382-
_relayData.exclusiveRelayer,
383-
_depositId,
384-
_quoteTimestamp,
385-
_relayData.fillDeadline,
386-
_relayData.exclusivityDeadline,
387-
_relayData.message,
388-
];
389-
}
390369
beforeEach(async function () {
391370
relayData = {
392371
depositor: depositor.address,
@@ -597,45 +576,6 @@ describe("SpokePool Depositor Logic", async function () {
597576
"ReentrancyGuard: reentrant call"
598577
);
599578
});
600-
it("unsafe deposit ID", async function () {
601-
const currentTime = (await spokePool.getCurrentTime()).toNumber();
602-
// new deposit ID should be the uint256 equivalent of the keccak256 hash of packed {msg.sender, depositor, forcedDepositId}.
603-
const forcedDepositId = "99";
604-
const expectedDepositId = BigNumber.from(
605-
ethers.utils.solidityKeccak256(
606-
["address", "address", "uint256"],
607-
[depositor.address, recipient.address, forcedDepositId]
608-
)
609-
);
610-
expect(await spokePool.getUnsafeDepositId(depositor.address, recipient.address, forcedDepositId)).to.equal(
611-
expectedDepositId
612-
);
613-
// Note: we deliberately set the depositor != msg.sender to test that the hashing algorithm correctly includes
614-
// both addresses in the hash.
615-
await expect(
616-
spokePool
617-
.connect(depositor)
618-
.unsafeDepositV3(
619-
...getUnsafeDepositArgsFromRelayData({ ...relayData, depositor: recipient.address }, forcedDepositId)
620-
)
621-
)
622-
.to.emit(spokePool, "V3FundsDeposited")
623-
.withArgs(
624-
relayData.inputToken,
625-
relayData.outputToken,
626-
relayData.inputAmount,
627-
relayData.outputAmount,
628-
destinationChainId,
629-
expectedDepositId,
630-
quoteTimestamp,
631-
relayData.fillDeadline,
632-
currentTime,
633-
recipient.address,
634-
relayData.recipient,
635-
relayData.exclusiveRelayer,
636-
relayData.message
637-
);
638-
});
639579
});
640580
describe("speed up V3 deposit", function () {
641581
const updatedOutputAmount = amountToDeposit.add(1);

0 commit comments

Comments
 (0)