@arbitrum/nitro-contracts 1.0.0-beta.8 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/hardhat.prod-config.js +4 -4
  3. package/package.json +21 -3
  4. package/src/bridge/Bridge.sol +39 -30
  5. package/src/bridge/IBridge.sol +56 -29
  6. package/src/bridge/IInbox.sol +130 -15
  7. package/src/bridge/IOutbox.sol +55 -7
  8. package/src/bridge/ISequencerInbox.sol +84 -7
  9. package/src/bridge/Inbox.sol +233 -93
  10. package/src/bridge/Outbox.sol +23 -47
  11. package/src/bridge/SequencerInbox.sol +132 -62
  12. package/src/challenge/ChallengeManager.sol +0 -9
  13. package/src/challenge/IChallengeManager.sol +0 -2
  14. package/src/libraries/AdminFallbackProxy.sol +4 -4
  15. package/src/libraries/Constants.sol +6 -0
  16. package/src/libraries/{SecondaryLogicUUPSUpgradeable.sol → DoubleLogicUUPSUpgradeable.sol} +2 -1
  17. package/src/libraries/Error.sol +12 -0
  18. package/src/libraries/IGasRefunder.sol +11 -5
  19. package/src/libraries/MerkleLib.sol +5 -3
  20. package/src/mocks/BridgeStub.sol +20 -1
  21. package/src/mocks/BridgeUnproxied.sol +17 -0
  22. package/src/mocks/InboxStub.sol +48 -3
  23. package/src/mocks/SequencerInboxStub.sol +13 -3
  24. package/src/mocks/Simple.sol +69 -0
  25. package/src/node-interface/NodeInterface.sol +35 -4
  26. package/src/precompiles/ArbGasInfo.sol +7 -4
  27. package/src/precompiles/ArbOwner.sol +9 -0
  28. package/src/precompiles/ArbOwnerPublic.sol +3 -0
  29. package/src/precompiles/ArbSys.sol +5 -2
  30. package/src/rollup/IRollupCore.sol +2 -0
  31. package/src/rollup/IRollupLogic.sol +10 -0
  32. package/src/rollup/RollupAdminLogic.sol +27 -3
  33. package/src/rollup/RollupCore.sol +3 -0
  34. package/src/rollup/RollupCreator.sol +3 -3
  35. package/src/rollup/RollupEventInbox.sol +3 -6
  36. package/src/{libraries/ArbitrumProxy.sol → rollup/RollupProxy.sol} +3 -3
  37. package/src/rollup/RollupUserLogic.sol +47 -10
  38. package/src/test-helpers/BridgeTester.sol +7 -1
  39. package/src/test-helpers/OutboxWithoutOptTester.sol +8 -8
@@ -8,6 +8,7 @@ pragma experimental ABIEncoderV2;
8
8
 
9
9
  import "../libraries/IGasRefunder.sol";
10
10
  import "./IDelayedMessageProvider.sol";
11
+ import "./IBridge.sol";
11
12
 
12
13
  interface ISequencerInbox is IDelayedMessageProvider {
13
14
  struct MaxTimeVariation {
@@ -51,33 +52,109 @@ interface ISequencerInbox is IDelayedMessageProvider {
51
52
  /// @dev a keyset was invalidated
52
53
  event InvalidateKeyset(bytes32 indexed keysetHash);
53
54
 
55
+ function totalDelayedMessagesRead() external view returns (uint256);
56
+
57
+ function bridge() external view returns (IBridge);
58
+
59
+ /// @dev The size of the batch header
60
+ // solhint-disable-next-line func-name-mixedcase
61
+ function HEADER_LENGTH() external view returns (uint256);
62
+
63
+ /// @dev If the first batch data byte after the header has this bit set,
64
+ /// the sequencer inbox has authenticated the data. Currently not used.
65
+ // solhint-disable-next-line func-name-mixedcase
66
+ function DATA_AUTHENTICATED_FLAG() external view returns (bytes1);
67
+
68
+ function rollup() external view returns (IOwnable);
69
+
70
+ function isBatchPoster(address) external view returns (bool);
71
+
72
+ struct DasKeySetInfo {
73
+ bool isValidKeyset;
74
+ uint64 creationBlock;
75
+ }
76
+
77
+ // https://github.com/ethereum/solidity/issues/11826
78
+ // function maxTimeVariation() external view returns (MaxTimeVariation calldata);
79
+ // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata);
80
+
81
+ /// @notice Remove force inclusion delay after a L1 chainId fork
82
+ function removeDelayAfterFork() external;
83
+
84
+ /// @notice Force messages from the delayed inbox to be included in the chain
85
+ /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and
86
+ /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these
87
+ /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages.
88
+ /// @param _totalDelayedMessagesRead The total number of messages to read up to
89
+ /// @param kind The kind of the last message to be included
90
+ /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
91
+ /// @param baseFeeL1 The l1 gas price of the last message to be included
92
+ /// @param sender The sender of the last message to be included
93
+ /// @param messageDataHash The messageDataHash of the last message to be included
94
+ function forceInclusion(
95
+ uint256 _totalDelayedMessagesRead,
96
+ uint8 kind,
97
+ uint64[2] calldata l1BlockAndTime,
98
+ uint256 baseFeeL1,
99
+ address sender,
100
+ bytes32 messageDataHash
101
+ ) external;
102
+
54
103
  function inboxAccs(uint256 index) external view returns (bytes32);
55
104
 
56
105
  function batchCount() external view returns (uint256);
57
106
 
58
- function addSequencerL2Batch(
107
+ function isValidKeysetHash(bytes32 ksHash) external view returns (bool);
108
+
109
+ /// @notice the creation block is intended to still be available after a keyset is deleted
110
+ function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256);
111
+
112
+ // ---------- BatchPoster functions ----------
113
+
114
+ function addSequencerL2BatchFromOrigin(
59
115
  uint256 sequenceNumber,
60
116
  bytes calldata data,
61
117
  uint256 afterDelayedMessagesRead,
62
118
  IGasRefunder gasRefunder
63
119
  ) external;
64
120
 
65
- // Methods only callable by rollup owner
121
+ function addSequencerL2Batch(
122
+ uint256 sequenceNumber,
123
+ bytes calldata data,
124
+ uint256 afterDelayedMessagesRead,
125
+ IGasRefunder gasRefunder,
126
+ uint256 prevMessageCount,
127
+ uint256 newMessageCount
128
+ ) external;
129
+
130
+ // ---------- onlyRollupOrOwner functions ----------
66
131
 
67
132
  /**
68
- * @notice Set max time variation from actual time for sequencer inbox
69
- * @param timeVariation the maximum time variation parameters
133
+ * @notice Set max delay for sequencer inbox
134
+ * @param maxTimeVariation_ the maximum time variation parameters
70
135
  */
71
- function setMaxTimeVariation(MaxTimeVariation memory timeVariation) external;
136
+ function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external;
72
137
 
73
138
  /**
74
139
  * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
75
140
  * @param addr the address
76
- * @param isBatchPoster if the specified address should be authorized as a batch poster
141
+ * @param isBatchPoster_ if the specified address should be authorized as a batch poster
77
142
  */
78
- function setIsBatchPoster(address addr, bool isBatchPoster) external;
143
+ function setIsBatchPoster(address addr, bool isBatchPoster_) external;
79
144
 
145
+ /**
146
+ * @notice Makes Data Availability Service keyset valid
147
+ * @param keysetBytes bytes of the serialized keyset
148
+ */
80
149
  function setValidKeyset(bytes calldata keysetBytes) external;
81
150
 
151
+ /**
152
+ * @notice Invalidates a Data Availability Service keyset
153
+ * @param ksHash hash of the keyset
154
+ */
82
155
  function invalidateKeysetHash(bytes32 ksHash) external;
156
+
157
+ // ---------- initializer ----------
158
+
159
+ function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
83
160
  }
@@ -15,7 +15,10 @@ import {
15
15
  InsufficientSubmissionCost,
16
16
  NotAllowedOrigin,
17
17
  RetryableData,
18
- NotRollupOrOwner
18
+ NotRollupOrOwner,
19
+ L1Forked,
20
+ NotForked,
21
+ GasLimitTooLarge
19
22
  } from "../libraries/Error.sol";
20
23
  import "./IInbox.sol";
21
24
  import "./ISequencerInbox.sol";
@@ -32,7 +35,8 @@ import {
32
35
  L2MessageType_unsignedEOATx,
33
36
  L2MessageType_unsignedContractTx
34
37
  } from "../libraries/MessageTypes.sol";
35
- import {MAX_DATA_SIZE} from "../libraries/Constants.sol";
38
+ import {MAX_DATA_SIZE, UNISWAP_L1_TIMELOCK, UNISWAP_L2_FACTORY} from "../libraries/Constants.sol";
39
+ import "../precompiles/ArbSys.sol";
36
40
 
37
41
  import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
38
42
  import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
@@ -43,7 +47,7 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
43
47
  * to await inclusion in the SequencerInbox
44
48
  */
45
49
  contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
46
- IBridge public override bridge;
50
+ IBridge public bridge;
47
51
  ISequencerInbox public sequencerInbox;
48
52
 
49
53
  /// ------------------------------------ allow list start ------------------------------------ ///
@@ -92,12 +96,18 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
92
96
  _;
93
97
  }
94
98
 
95
- /// @notice pauses all inbox functionality
99
+ uint256 internal immutable deployTimeChainId = block.chainid;
100
+
101
+ function _chainIdChanged() internal view returns (bool) {
102
+ return deployTimeChainId != block.chainid;
103
+ }
104
+
105
+ /// @inheritdoc IInbox
96
106
  function pause() external onlyRollupOrOwner {
97
107
  _pause();
98
108
  }
99
109
 
100
- /// @notice unpauses all inbox functionality
110
+ /// @inheritdoc IInbox
101
111
  function unpause() external onlyRollupOrOwner {
102
112
  _unpause();
103
113
  }
@@ -107,37 +117,23 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
107
117
  initializer
108
118
  onlyDelegated
109
119
  {
110
- if (address(bridge) != address(0)) revert AlreadyInit();
111
120
  bridge = _bridge;
112
121
  sequencerInbox = _sequencerInbox;
113
122
  allowListEnabled = false;
114
123
  __Pausable_init();
115
124
  }
116
125
 
117
- /// @dev function to be called one time during the inbox upgrade process
118
- /// this is used to fix the storage slots
119
- function postUpgradeInit(IBridge _bridge) external onlyDelegated onlyProxyOwner {
120
- uint8 slotsToWipe = 3;
121
- for (uint8 i = 0; i < slotsToWipe; i++) {
122
- assembly {
123
- sstore(i, 0)
124
- }
125
- }
126
- allowListEnabled = false;
127
- bridge = _bridge;
128
- }
126
+ /// @inheritdoc IInbox
127
+ function postUpgradeInit(IBridge) external onlyDelegated onlyProxyOwner {}
129
128
 
130
- /**
131
- * @notice Send a generic L2 message to the chain
132
- * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
133
- * @param messageData Data of the message being sent
134
- */
129
+ /// @inheritdoc IInbox
135
130
  function sendL2MessageFromOrigin(bytes calldata messageData)
136
131
  external
137
132
  whenNotPaused
138
133
  onlyAllowed
139
134
  returns (uint256)
140
135
  {
136
+ if (_chainIdChanged()) revert L1Forked();
141
137
  // solhint-disable-next-line avoid-tx-origin
142
138
  if (msg.sender != tx.origin) revert NotOrigin();
143
139
  if (messageData.length > MAX_DATA_SIZE)
@@ -147,18 +143,14 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
147
143
  return msgNum;
148
144
  }
149
145
 
150
- /**
151
- * @notice Send a generic L2 message to the chain
152
- * @dev This method can be used to send any type of message that doesn't require L1 validation
153
- * @param messageData Data of the message being sent
154
- */
146
+ /// @inheritdoc IInbox
155
147
  function sendL2Message(bytes calldata messageData)
156
148
  external
157
- override
158
149
  whenNotPaused
159
150
  onlyAllowed
160
151
  returns (uint256)
161
152
  {
153
+ if (_chainIdChanged()) revert L1Forked();
162
154
  return _deliverMessage(L2_MSG, msg.sender, messageData);
163
155
  }
164
156
 
@@ -168,7 +160,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
168
160
  uint256 nonce,
169
161
  address to,
170
162
  bytes calldata data
171
- ) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
163
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
164
+ // arbos will discard unsigned tx with gas limit too large
165
+ if (gasLimit > type(uint64).max) {
166
+ revert GasLimitTooLarge();
167
+ }
172
168
  return
173
169
  _deliverMessage(
174
170
  L1MessageType_L2FundedByL1,
@@ -190,7 +186,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
190
186
  uint256 maxFeePerGas,
191
187
  address to,
192
188
  bytes calldata data
193
- ) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
189
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
190
+ // arbos will discard unsigned tx with gas limit too large
191
+ if (gasLimit > type(uint64).max) {
192
+ revert GasLimitTooLarge();
193
+ }
194
194
  return
195
195
  _deliverMessage(
196
196
  L1MessageType_L2FundedByL1,
@@ -213,7 +213,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
213
213
  address to,
214
214
  uint256 value,
215
215
  bytes calldata data
216
- ) external virtual override whenNotPaused onlyAllowed returns (uint256) {
216
+ ) external whenNotPaused onlyAllowed returns (uint256) {
217
+ // arbos will discard unsigned tx with gas limit too large
218
+ if (gasLimit > type(uint64).max) {
219
+ revert GasLimitTooLarge();
220
+ }
217
221
  return
218
222
  _deliverMessage(
219
223
  L2_MSG,
@@ -236,7 +240,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
236
240
  address to,
237
241
  uint256 value,
238
242
  bytes calldata data
239
- ) external virtual override whenNotPaused onlyAllowed returns (uint256) {
243
+ ) external whenNotPaused onlyAllowed returns (uint256) {
244
+ // arbos will discard unsigned tx with gas limit too large
245
+ if (gasLimit > type(uint64).max) {
246
+ revert GasLimitTooLarge();
247
+ }
240
248
  return
241
249
  _deliverMessage(
242
250
  L2_MSG,
@@ -252,13 +260,104 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
252
260
  );
253
261
  }
254
262
 
255
- /**
256
- * @notice Get the L1 fee for submitting a retryable
257
- * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value
258
- * @dev This formula may change in the future, to future proof your code query this method instead of inlining!!
259
- * @param dataLength The length of the retryable's calldata, in bytes
260
- * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used
261
- */
263
+ /// @inheritdoc IInbox
264
+ function sendL1FundedUnsignedTransactionToFork(
265
+ uint256 gasLimit,
266
+ uint256 maxFeePerGas,
267
+ uint256 nonce,
268
+ address to,
269
+ bytes calldata data
270
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
271
+ if (!_chainIdChanged()) revert NotForked();
272
+ // solhint-disable-next-line avoid-tx-origin
273
+ if (msg.sender != tx.origin) revert NotOrigin();
274
+ // arbos will discard unsigned tx with gas limit too large
275
+ if (gasLimit > type(uint64).max) {
276
+ revert GasLimitTooLarge();
277
+ }
278
+ return
279
+ _deliverMessage(
280
+ L1MessageType_L2FundedByL1,
281
+ // undoing sender alias here to cancel out the aliasing
282
+ AddressAliasHelper.undoL1ToL2Alias(msg.sender),
283
+ abi.encodePacked(
284
+ L2MessageType_unsignedEOATx,
285
+ gasLimit,
286
+ maxFeePerGas,
287
+ nonce,
288
+ uint256(uint160(to)),
289
+ msg.value,
290
+ data
291
+ )
292
+ );
293
+ }
294
+
295
+ /// @inheritdoc IInbox
296
+ function sendUnsignedTransactionToFork(
297
+ uint256 gasLimit,
298
+ uint256 maxFeePerGas,
299
+ uint256 nonce,
300
+ address to,
301
+ uint256 value,
302
+ bytes calldata data
303
+ ) external whenNotPaused onlyAllowed returns (uint256) {
304
+ if (!_chainIdChanged()) revert NotForked();
305
+ // solhint-disable-next-line avoid-tx-origin
306
+ if (msg.sender != tx.origin) revert NotOrigin();
307
+ // arbos will discard unsigned tx with gas limit too large
308
+ if (gasLimit > type(uint64).max) {
309
+ revert GasLimitTooLarge();
310
+ }
311
+ return
312
+ _deliverMessage(
313
+ L2_MSG,
314
+ // undoing sender alias here to cancel out the aliasing
315
+ AddressAliasHelper.undoL1ToL2Alias(msg.sender),
316
+ abi.encodePacked(
317
+ L2MessageType_unsignedEOATx,
318
+ gasLimit,
319
+ maxFeePerGas,
320
+ nonce,
321
+ uint256(uint160(to)),
322
+ value,
323
+ data
324
+ )
325
+ );
326
+ }
327
+
328
+ /// @inheritdoc IInbox
329
+ function sendWithdrawEthToFork(
330
+ uint256 gasLimit,
331
+ uint256 maxFeePerGas,
332
+ uint256 nonce,
333
+ uint256 value,
334
+ address withdrawTo
335
+ ) external whenNotPaused onlyAllowed returns (uint256) {
336
+ if (!_chainIdChanged()) revert NotForked();
337
+ // solhint-disable-next-line avoid-tx-origin
338
+ if (msg.sender != tx.origin) revert NotOrigin();
339
+ // arbos will discard unsigned tx with gas limit too large
340
+ if (gasLimit > type(uint64).max) {
341
+ revert GasLimitTooLarge();
342
+ }
343
+ return
344
+ _deliverMessage(
345
+ L2_MSG,
346
+ // undoing sender alias here to cancel out the aliasing
347
+ AddressAliasHelper.undoL1ToL2Alias(msg.sender),
348
+ abi.encodePacked(
349
+ L2MessageType_unsignedEOATx,
350
+ gasLimit,
351
+ maxFeePerGas,
352
+ nonce,
353
+ uint256(uint160(address(100))), // ArbSys address
354
+ value,
355
+ abi.encode(ArbSys.withdrawEth.selector, withdrawTo)
356
+ )
357
+ );
358
+ }
359
+
360
+ /// @inheritdoc IInbox
262
361
  function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
263
362
  public
264
363
  view
@@ -268,20 +367,13 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
268
367
  return (1400 + 6 * dataLength) * (baseFee == 0 ? block.basefee : baseFee);
269
368
  }
270
369
 
271
- /// @notice deposit eth from L1 to L2
272
- /// @dev this does not trigger the fallback function when receiving in the L2 side.
273
- /// Look into retryable tickets if you are interested in this functionality.
274
- /// @dev this function should not be called inside contract constructors
275
- function depositEth() public payable override whenNotPaused onlyAllowed returns (uint256) {
370
+ /// @inheritdoc IInbox
371
+ function depositEth() public payable whenNotPaused onlyAllowed returns (uint256) {
276
372
  address dest = msg.sender;
277
373
 
278
374
  // solhint-disable-next-line avoid-tx-origin
279
375
  if (AddressUpgradeable.isContract(msg.sender) || tx.origin != msg.sender) {
280
376
  // isContract check fails if this function is called during a contract's constructor.
281
- // We don't adjust the address for calls coming from L1 contracts since their addresses get remapped
282
- // If the caller is an EOA, we adjust the address.
283
- // This is needed because unsigned messages to the L2 (such as retryables)
284
- // have the L1 sender address mapped.
285
377
  dest = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
286
378
  }
287
379
 
@@ -294,15 +386,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
294
386
  }
295
387
 
296
388
  /// @notice deprecated in favour of depositEth with no parameters
297
- function depositEth(uint256)
298
- external
299
- payable
300
- virtual
301
- override
302
- whenNotPaused
303
- onlyAllowed
304
- returns (uint256)
305
- {
389
+ function depositEth(uint256) external payable whenNotPaused onlyAllowed returns (uint256) {
306
390
  return depositEth();
307
391
  }
308
392
 
@@ -318,7 +402,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
318
402
  * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
319
403
  * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
320
404
  * @param data ABI encoded data of L2 message
321
- * @return unique id for retryable transaction (keccak256(requestID, uint(0) )
405
+ * @return unique message number of the retryable transaction
322
406
  */
323
407
  function createRetryableTicketNoRefundAliasRewrite(
324
408
  address to,
@@ -329,7 +413,8 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
329
413
  uint256 gasLimit,
330
414
  uint256 maxFeePerGas,
331
415
  bytes calldata data
332
- ) external payable virtual whenNotPaused onlyAllowed returns (uint256) {
416
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
417
+ // gas limit is validated to be within uint64 in unsafeCreateRetryableTicket
333
418
  return
334
419
  unsafeCreateRetryableTicket(
335
420
  to,
@@ -343,20 +428,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
343
428
  );
344
429
  }
345
430
 
346
- /**
347
- * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
348
- * @dev all msg.value will deposited to callValueRefundAddress on L2
349
- * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
350
- * @param to destination L2 contract address
351
- * @param l2CallValue call value for retryable L2 message
352
- * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
353
- * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
354
- * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
355
- * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
356
- * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
357
- * @param data ABI encoded data of L2 message
358
- * @return unique id for retryable transaction (keccak256(requestID, uint(0) )
359
- */
431
+ /// @inheritdoc IInbox
360
432
  function createRetryableTicket(
361
433
  address to,
362
434
  uint256 l2CallValue,
@@ -366,7 +438,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
366
438
  uint256 gasLimit,
367
439
  uint256 maxFeePerGas,
368
440
  bytes calldata data
369
- ) external payable virtual override whenNotPaused onlyAllowed returns (uint256) {
441
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
370
442
  // ensure the user's deposit alone will make submission succeed
371
443
  if (msg.value < (maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas)) {
372
444
  revert InsufficientValue(
@@ -386,6 +458,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
386
458
  callValueRefundAddress = AddressAliasHelper.applyL1ToL2Alias(callValueRefundAddress);
387
459
  }
388
460
 
461
+ // gas limit is validated to be within uint64 in unsafeCreateRetryableTicket
389
462
  return
390
463
  unsafeCreateRetryableTicket(
391
464
  to,
@@ -399,23 +472,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
399
472
  );
400
473
  }
401
474
 
402
- /**
403
- * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
404
- * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds
405
- * come from the deposit alone, rather than falling back on the user's L2 balance
406
- * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
407
- * createRetryableTicket method is the recommended standard.
408
- * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
409
- * @param to destination L2 contract address
410
- * @param l2CallValue call value for retryable L2 message
411
- * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
412
- * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
413
- * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
414
- * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
415
- * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
416
- * @param data ABI encoded data of L2 message
417
- * @return unique id for retryable transaction (keccak256(requestID, uint(0) )
418
- */
475
+ /// @inheritdoc IInbox
419
476
  function unsafeCreateRetryableTicket(
420
477
  address to,
421
478
  uint256 l2CallValue,
@@ -425,7 +482,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
425
482
  uint256 gasLimit,
426
483
  uint256 maxFeePerGas,
427
484
  bytes calldata data
428
- ) public payable virtual override whenNotPaused onlyAllowed returns (uint256) {
485
+ ) public payable whenNotPaused onlyAllowed returns (uint256) {
429
486
  // gas price and limit of 1 should never be a valid input, so instead they are used as
430
487
  // magic values to trigger a revert in eth calls that surface data without requiring a tx trace
431
488
  if (gasLimit == 1 || maxFeePerGas == 1)
@@ -442,6 +499,11 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
442
499
  data
443
500
  );
444
501
 
502
+ // arbos will discard retryable with gas limit too large
503
+ if (gasLimit > type(uint64).max) {
504
+ revert GasLimitTooLarge();
505
+ }
506
+
445
507
  uint256 submissionFee = calculateRetryableSubmissionFee(data.length, block.basefee);
446
508
  if (maxSubmissionCost < submissionFee)
447
509
  revert InsufficientSubmissionCost(submissionFee, maxSubmissionCost);
@@ -465,6 +527,84 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
465
527
  );
466
528
  }
467
529
 
530
+ /// @notice This is an one-time-exception to resolve a misconfiguration of Uniswap Arbitrum deployment
531
+ /// Only the Uniswap L1 Timelock may call this function and it is allowed to create a crosschain
532
+ /// retryable ticket without address aliasing. More info here:
533
+ /// https://gov.uniswap.org/t/consensus-check-fix-the-cross-chain-messaging-bridge-on-arbitrum/18547
534
+ /// @dev This function will be removed in future releases
535
+ function uniswapCreateRetryableTicket(
536
+ address to,
537
+ uint256 l2CallValue,
538
+ uint256 maxSubmissionCost,
539
+ address excessFeeRefundAddress,
540
+ address callValueRefundAddress,
541
+ uint256 gasLimit,
542
+ uint256 maxFeePerGas,
543
+ bytes calldata data
544
+ ) external payable whenNotPaused onlyAllowed returns (uint256) {
545
+ // this can only be called by UNISWAP_L1_TIMELOCK
546
+ require(msg.sender == UNISWAP_L1_TIMELOCK, "NOT_UNISWAP_L1_TIMELOCK");
547
+ // the retryable can only call UNISWAP_L2_FACTORY
548
+ require(to == UNISWAP_L2_FACTORY, "NOT_TO_UNISWAP_L2_FACTORY");
549
+
550
+ // ensure the user's deposit alone will make submission succeed
551
+ if (msg.value < (maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas)) {
552
+ revert InsufficientValue(
553
+ maxSubmissionCost + l2CallValue + gasLimit * maxFeePerGas,
554
+ msg.value
555
+ );
556
+ }
557
+
558
+ // if a refund address is a contract, we apply the alias to it
559
+ // so that it can access its funds on the L2
560
+ // since the beneficiary and other refund addresses don't get rewritten by arb-os
561
+ if (AddressUpgradeable.isContract(excessFeeRefundAddress)) {
562
+ excessFeeRefundAddress = AddressAliasHelper.applyL1ToL2Alias(excessFeeRefundAddress);
563
+ }
564
+ if (AddressUpgradeable.isContract(callValueRefundAddress)) {
565
+ // this is the beneficiary. be careful since this is the address that can cancel the retryable in the L2
566
+ callValueRefundAddress = AddressAliasHelper.applyL1ToL2Alias(callValueRefundAddress);
567
+ }
568
+
569
+ // gas price and limit of 1 should never be a valid input, so instead they are used as
570
+ // magic values to trigger a revert in eth calls that surface data without requiring a tx trace
571
+ if (gasLimit == 1 || maxFeePerGas == 1)
572
+ revert RetryableData(
573
+ msg.sender,
574
+ to,
575
+ l2CallValue,
576
+ msg.value,
577
+ maxSubmissionCost,
578
+ excessFeeRefundAddress,
579
+ callValueRefundAddress,
580
+ gasLimit,
581
+ maxFeePerGas,
582
+ data
583
+ );
584
+
585
+ uint256 submissionFee = calculateRetryableSubmissionFee(data.length, block.basefee);
586
+ if (maxSubmissionCost < submissionFee)
587
+ revert InsufficientSubmissionCost(submissionFee, maxSubmissionCost);
588
+
589
+ return
590
+ _deliverMessage(
591
+ L1MessageType_submitRetryableTx,
592
+ AddressAliasHelper.undoL1ToL2Alias(msg.sender),
593
+ abi.encodePacked(
594
+ uint256(uint160(to)),
595
+ l2CallValue,
596
+ msg.value,
597
+ maxSubmissionCost,
598
+ uint256(uint160(excessFeeRefundAddress)),
599
+ uint256(uint160(callValueRefundAddress)),
600
+ gasLimit,
601
+ maxFeePerGas,
602
+ data.length,
603
+ data
604
+ )
605
+ );
606
+ }
607
+
468
608
  function _deliverMessage(
469
609
  uint8 _kind,
470
610
  address _sender,