Skip to content

Commit 5a0c67c

Browse files
authored
Revert "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)" (#638)
1 parent 4fa38e3 commit 5a0c67c

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
@@ -127,7 +127,7 @@ abstract contract SpokePool is
127127

128128
bytes32 public constant UPDATE_V3_DEPOSIT_DETAILS_HASH =
129129
keccak256(
130-
"UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
130+
"UpdateDepositDetails(uint32 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
131131
);
132132

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

1129-
/**
1130-
* @notice Returns the deposit ID for an unsafe deposit. This function is used to compute the deposit ID
1131-
* in unsafeDepositV3 and is provided as a convenience.
1132-
* @dev msgSenderand depositor are both used as inputs to allow passthrough depositors to create unique
1133-
* deposit hash spaces for unique depositors.
1134-
* @param msgSender The caller of the transaction used as input to produce the deposit ID.
1135-
* @param depositor The depositor address used as input to produce the deposit ID.
1136-
* @param depositNonce The nonce used as input to produce the deposit ID.
1137-
* @return The deposit ID for the unsafe deposit.
1138-
*/
1139-
function getUnsafeDepositId(
1140-
address msgSender,
1141-
address depositor,
1142-
uint256 depositNonce
1143-
) public pure returns (uint256) {
1144-
return uint256(keccak256(abi.encodePacked(msgSender, depositor, depositNonce)));
1145-
}
1146-
11471096
/**************************************
11481097
* INTERNAL FUNCTIONS *
11491098
**************************************/
11501099

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

13461224
function _verifyUpdateV3DepositMessage(
13471225
address depositor,
1348-
uint256 depositId,
1226+
uint32 depositId,
13491227
uint256 originChainId,
13501228
uint256 updatedOutputAmount,
13511229
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)