@arbitrum/nitro-contracts 1.0.2 → 1.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,164 @@
1
+ import '@nomiclabs/hardhat-waffle'
2
+ import 'hardhat-deploy'
3
+ import '@nomiclabs/hardhat-ethers'
4
+ import '@nomiclabs/hardhat-etherscan'
5
+ import '@typechain/hardhat'
6
+ import 'solidity-coverage'
7
+ import 'hardhat-gas-reporter'
8
+
9
+ const solidity = {
10
+ compilers: [
11
+ {
12
+ version: '0.8.9',
13
+ settings: {
14
+ optimizer: {
15
+ enabled: true,
16
+ runs: 100,
17
+ },
18
+ },
19
+ },
20
+ ],
21
+ overrides: {},
22
+ }
23
+
24
+ if (process.env['INTERFACE_TESTER_SOLC_VERSION']) {
25
+ solidity.compilers.push({
26
+ version: process.env['INTERFACE_TESTER_SOLC_VERSION'],
27
+ settings: {
28
+ optimizer: {
29
+ enabled: true,
30
+ runs: 100,
31
+ },
32
+ },
33
+ })
34
+ solidity.overrides = {
35
+ 'src/test-helpers/InterfaceCompatibilityTester.sol': {
36
+ version: process.env['INTERFACE_TESTER_SOLC_VERSION'],
37
+ settings: {
38
+ optimizer: {
39
+ enabled: true,
40
+ runs: 100,
41
+ },
42
+ },
43
+ },
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @type import('hardhat/config').HardhatUserConfig
49
+ */
50
+ module.exports = {
51
+ solidity,
52
+ paths: {
53
+ sources: './src',
54
+ artifacts: 'build/contracts',
55
+ },
56
+ namedAccounts: {
57
+ deployer: {
58
+ default: 0,
59
+ },
60
+ },
61
+ networks: {
62
+ hardhat: {
63
+ chainId: 1338,
64
+ throwOnTransactionFailures: true,
65
+ allowUnlimitedContractSize: true,
66
+ accounts: {
67
+ accountsBalance: '1000000000000000000000000000',
68
+ },
69
+ blockGasLimit: 200000000,
70
+ // mining: {
71
+ // auto: false,
72
+ // interval: 1000,
73
+ // },
74
+ forking: {
75
+ url: 'https://mainnet.infura.io/v3/' + process.env['INFURA_KEY'],
76
+ enabled: process.env['SHOULD_FORK'] === '1',
77
+ },
78
+ },
79
+ mainnet: {
80
+ url: 'https://mainnet.infura.io/v3/' + process.env['INFURA_KEY'],
81
+ accounts: process.env['MAINNET_PRIVKEY']
82
+ ? [process.env['MAINNET_PRIVKEY']]
83
+ : [],
84
+ },
85
+ goerli: {
86
+ url: 'https://goerli.infura.io/v3/' + process.env['INFURA_KEY'],
87
+ accounts: process.env['DEVNET_PRIVKEY']
88
+ ? [process.env['DEVNET_PRIVKEY']]
89
+ : [],
90
+ },
91
+ rinkeby: {
92
+ url: 'https://rinkeby.infura.io/v3/' + process.env['INFURA_KEY'],
93
+ accounts: process.env['DEVNET_PRIVKEY']
94
+ ? [process.env['DEVNET_PRIVKEY']]
95
+ : [],
96
+ },
97
+ arbRinkeby: {
98
+ url: 'https://rinkeby.arbitrum.io/rpc',
99
+ accounts: process.env['DEVNET_PRIVKEY']
100
+ ? [process.env['DEVNET_PRIVKEY']]
101
+ : [],
102
+ },
103
+ arbGoerliRollup: {
104
+ url: 'https://goerli-rollup.arbitrum.io/rpc',
105
+ accounts: process.env['DEVNET_PRIVKEY']
106
+ ? [process.env['DEVNET_PRIVKEY']]
107
+ : [],
108
+ },
109
+ arb1: {
110
+ url: 'https://arb1.arbitrum.io/rpc',
111
+ accounts: process.env['MAINNET_PRIVKEY']
112
+ ? [process.env['MAINNET_PRIVKEY']]
113
+ : [],
114
+ },
115
+ nova: {
116
+ url: 'https://nova.arbitrum.io/rpc',
117
+ accounts: process.env['MAINNET_PRIVKEY']
118
+ ? [process.env['MAINNET_PRIVKEY']]
119
+ : [],
120
+ },
121
+ geth: {
122
+ url: 'http://localhost:8545',
123
+ },
124
+ },
125
+ etherscan: {
126
+ apiKey: {
127
+ mainnet: process.env['ETHERSCAN_API_KEY'],
128
+ goerli: process.env['ETHERSCAN_API_KEY'],
129
+ rinkeby: process.env['ETHERSCAN_API_KEY'],
130
+ arbitrumOne: process.env['ARBISCAN_API_KEY'],
131
+ arbitrumTestnet: process.env['ARBISCAN_API_KEY'],
132
+ nova: process.env['NOVA_ARBISCAN_API_KEY'],
133
+ arbGoerliRollup: process.env['ARBISCAN_API_KEY'],
134
+ },
135
+ customChains: [
136
+ {
137
+ network: 'nova',
138
+ chainId: 42170,
139
+ urls: {
140
+ apiURL: 'https://api-nova.arbiscan.io/api',
141
+ browserURL: 'https://nova.arbiscan.io/',
142
+ },
143
+ },
144
+ {
145
+ network: 'arbGoerliRollup',
146
+ chainId: 421613,
147
+ urls: {
148
+ apiURL: 'https://api-goerli.arbiscan.io/api',
149
+ browserURL: 'https://goerli.arbiscan.io/',
150
+ },
151
+ },
152
+ ],
153
+ },
154
+ mocha: {
155
+ timeout: 0,
156
+ },
157
+ gasReporter: {
158
+ enabled: process.env.DISABLE_GAS_REPORTER ? false : true,
159
+ },
160
+ typechain: {
161
+ outDir: 'build/types',
162
+ target: 'ethers-v5',
163
+ },
164
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbitrum/nitro-contracts",
3
- "version": "1.0.2",
3
+ "version": "1.1.0-alpha.1",
4
4
  "description": "Layer 2 precompiles and rollup for Arbitrum Nitro",
5
5
  "author": "Offchain Labs, Inc.",
6
6
  "license": "BUSL-1.1",
@@ -9,7 +9,8 @@
9
9
  "url": "git+https://github.com/offchainlabs/nitro-contracts.git"
10
10
  },
11
11
  "files": [
12
- "src/"
12
+ "src/",
13
+ "hardhat.config.ts"
13
14
  ],
14
15
  "bugs": {
15
16
  "url": "https://github.com/offchainlabs/nitro-contracts/issues"
@@ -60,8 +61,8 @@
60
61
  "solhint": "^3.3.7",
61
62
  "solhint-plugin-prettier": "^0.0.5",
62
63
  "solidity-coverage": "^0.7.20",
63
- "tslint": "^6.1.3",
64
64
  "ts-node": "^10.4.0",
65
+ "tslint": "^6.1.3",
65
66
  "typechain": "^8.0.0",
66
67
  "typescript": "^4.5.4"
67
68
  },
@@ -230,7 +230,7 @@ contract Bridge is Initializable, DelegateCallAware, IBridge {
230
230
  InOutInfo storage info = allowedDelayedInboxesMap[inbox];
231
231
  bool alreadyEnabled = info.allowed;
232
232
  emit InboxToggle(inbox, enabled);
233
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
233
+ if (alreadyEnabled == enabled) {
234
234
  return;
235
235
  }
236
236
  if (enabled) {
@@ -252,7 +252,7 @@ contract Bridge is Initializable, DelegateCallAware, IBridge {
252
252
  InOutInfo storage info = allowedOutboxesMap[outbox];
253
253
  bool alreadyEnabled = info.allowed;
254
254
  emit OutboxToggle(outbox, enabled);
255
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
255
+ if (alreadyEnabled == enabled) {
256
256
  return;
257
257
  }
258
258
  if (enabled) {
@@ -69,6 +69,8 @@ interface ISequencerInbox is IDelayedMessageProvider {
69
69
 
70
70
  function isBatchPoster(address) external view returns (bool);
71
71
 
72
+ function isSequencer(address) external view returns (bool);
73
+
72
74
  struct DasKeySetInfo {
73
75
  bool isValidKeyset;
74
76
  uint64 creationBlock;
@@ -154,6 +156,14 @@ interface ISequencerInbox is IDelayedMessageProvider {
154
156
  */
155
157
  function invalidateKeysetHash(bytes32 ksHash) external;
156
158
 
159
+ /**
160
+ * @notice Updates whether an address is authorized to be a sequencer.
161
+ * @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer.
162
+ * @param addr the address
163
+ * @param isSequencer_ if the specified address should be authorized as a sequencer
164
+ */
165
+ function setIsSequencer(address addr, bool isSequencer_) external;
166
+
157
167
  // ---------- initializer ----------
158
168
 
159
169
  function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
@@ -352,7 +352,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox {
352
352
  nonce,
353
353
  uint256(uint160(address(100))), // ArbSys address
354
354
  value,
355
- abi.encode(ArbSys.withdrawEth.selector, withdrawTo)
355
+ abi.encodeWithSelector(ArbSys.withdrawEth.selector, withdrawTo)
356
356
  )
357
357
  );
358
358
  }
@@ -64,6 +64,8 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
64
64
 
65
65
  uint256 internal immutable deployTimeChainId = block.chainid;
66
66
 
67
+ mapping(address => bool) public isSequencer;
68
+
67
69
  function _chainIdChanged() internal view returns (bool) {
68
70
  return deployTimeChainId != block.chainid;
69
71
  }
@@ -450,6 +452,12 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox
450
452
  emit OwnerFunctionCalled(3);
451
453
  }
452
454
 
455
+ /// @inheritdoc ISequencerInbox
456
+ function setIsSequencer(address addr, bool isSequencer_) external onlyRollupOwner {
457
+ isSequencer[addr] = isSequencer_;
458
+ emit OwnerFunctionCalled(4);
459
+ }
460
+
453
461
  function isValidKeysetHash(bytes32 ksHash) external view returns (bool) {
454
462
  return dasKeySetInfo[ksHash].isValidKeyset;
455
463
  }
@@ -107,13 +107,13 @@ contract AdminFallbackProxy is Proxy, DoubleLogicERC1967Upgrade {
107
107
  * Only the `adminAddr` is able to use the `adminLogic` functions
108
108
  * All other addresses can interact with the `userLogic` functions
109
109
  */
110
- constructor(
110
+ function _initialize(
111
111
  address adminLogic,
112
112
  bytes memory adminData,
113
113
  address userLogic,
114
114
  bytes memory userData,
115
115
  address adminAddr
116
- ) payable {
116
+ ) internal {
117
117
  assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
118
118
  assert(
119
119
  _IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
@@ -21,10 +21,7 @@ abstract contract GasRefundEnabled {
21
21
  uint256 startGasLeft = gasleft();
22
22
  _;
23
23
  if (address(gasRefunder) != address(0)) {
24
- uint256 calldataSize;
25
- assembly {
26
- calldataSize := calldatasize()
27
- }
24
+ uint256 calldataSize = msg.data.length;
28
25
  uint256 calldataWords = (calldataSize + 31) / 32;
29
26
  // account for the CALLDATACOPY cost of the proxy contract, including the memory expansion cost
30
27
  startGasLeft += calldataWords * 6 + (calldataWords**2) / 512;
@@ -139,7 +139,7 @@ contract BridgeStub is IBridge {
139
139
  InOutInfo storage info = allowedDelayedInboxesMap[inbox];
140
140
  bool alreadyEnabled = info.allowed;
141
141
  emit InboxToggle(inbox, enabled);
142
- if ((alreadyEnabled && enabled) || (!alreadyEnabled && !enabled)) {
142
+ if (alreadyEnabled == enabled) {
143
143
  return;
144
144
  }
145
145
  if (enabled) {
@@ -43,6 +43,10 @@ contract Simple {
43
43
  return block.number;
44
44
  }
45
45
 
46
+ function getBlockDifficulty() external view returns (uint256) {
47
+ return block.difficulty;
48
+ }
49
+
46
50
  function noop() external pure {}
47
51
 
48
52
  function pleaseRevert() external pure {
@@ -98,8 +98,10 @@ interface NodeInterface {
98
98
  /**
99
99
  * @notice Estimates a transaction's l1 costs.
100
100
  * @dev Use eth_call to call.
101
- * This method is exactly like gasEstimateComponents, but doesn't include the l2 component
101
+ * This method is similar to gasEstimateComponents, but doesn't include the l2 component
102
102
  * so that the l1 component can be known even when the tx may fail.
103
+ * This method also doesn't pad the estimate as gas estimation normally does.
104
+ * If using this value to submit a transaction, we'd recommend first padding it by 10%.
103
105
  * @param data the tx's calldata. Everything else like "From" and "Gas" are copied over
104
106
  * @param to the tx's "To" (ignored when contractCreation is true)
105
107
  * @param contractCreation whether "To" is omitted
@@ -84,6 +84,9 @@ interface ArbOwner {
84
84
  /// @notice Releases surplus funds from L1PricerFundsPoolAddress for use
85
85
  function releaseL1PricerSurplusFunds(uint256 maxWeiToRelease) external returns (uint256);
86
86
 
87
+ /// @notice Sets serialized chain config in ArbOS state
88
+ function setChainConfig(string calldata chainConfig) external;
89
+
87
90
  // Emitted when a successful call is made to this precompile
88
91
  event OwnerActs(bytes4 indexed method, address indexed owner, bytes data);
89
92
  }
@@ -84,6 +84,15 @@ interface IRollupCore {
84
84
  */
85
85
  function getNode(uint64 nodeNum) external view returns (Node memory);
86
86
 
87
+ /**
88
+ * @notice Returns the block in which the given node was created for looking up its creation event.
89
+ * Unlike the Node's createdAtBlock field, this will be the ArbSys blockNumber if the host chain is an Arbitrum chain.
90
+ * That means that the block number returned for this is usable for event queries.
91
+ * This function will revert if the given node number does not exist.
92
+ * @dev This function is meant for internal use only and has no stability guarantees.
93
+ */
94
+ function getNodeCreationBlockForLogLookup(uint64 nodeNum) external view returns (uint256);
95
+
87
96
  /**
88
97
  * @notice Check if the specified node has been staked on by the provided staker.
89
98
  * Only accurate at the latest confirmed node and afterwards.
@@ -13,5 +13,5 @@ interface IRollupEventInbox {
13
13
 
14
14
  function rollup() external view returns (address);
15
15
 
16
- function rollupInitialized(uint256 chainId) external;
16
+ function rollupInitialized(uint256 chainId, string calldata chainConfig) external;
17
17
  }
@@ -36,7 +36,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeabl
36
36
  true
37
37
  );
38
38
 
39
- connectedContracts.rollupEventInbox.rollupInitialized(config.chainId);
39
+ connectedContracts.rollupEventInbox.rollupInitialized(config.chainId, config.chainConfig);
40
40
  connectedContracts.sequencerInbox.addSequencerL2Batch(
41
41
  0,
42
42
  "",
@@ -18,6 +18,8 @@ import "../bridge/ISequencerInbox.sol";
18
18
  import "../bridge/IBridge.sol";
19
19
  import "../bridge/IOutbox.sol";
20
20
 
21
+ import "../precompiles/ArbSys.sol";
22
+
21
23
  import {NO_CHAL_INDEX} from "../libraries/Constants.sol";
22
24
 
23
25
  abstract contract RollupCore is IRollupCore, PausableUpgradeable {
@@ -76,6 +78,18 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
76
78
 
77
79
  bool public validatorWhitelistDisabled;
78
80
 
81
+ // If the chain this RollupCore is deployed on is an Arbitrum chain.
82
+ bool internal immutable _hostChainIsArbitrum;
83
+ // If the chain RollupCore is deployed on, this will contain the ArbSys.blockNumber() at each node's creation.
84
+ mapping(uint64 => uint256) internal _nodeCreatedAtArbSysBlock;
85
+
86
+ constructor() {
87
+ (bool ok, bytes memory data) = address(100).staticcall(
88
+ abi.encodeWithSelector(ArbSys.arbOSVersion.selector)
89
+ );
90
+ _hostChainIsArbitrum = ok && data.length == 32;
91
+ }
92
+
79
93
  /**
80
94
  * @notice Get a storage reference to the Node for the given node index
81
95
  * @param nodeNum Index of the node
@@ -92,6 +106,30 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
92
106
  return getNodeStorage(nodeNum);
93
107
  }
94
108
 
109
+ /**
110
+ * @notice Returns the block in which the given node was created for looking up its creation event.
111
+ * Unlike the Node's createdAtBlock field, this will be the ArbSys blockNumber if the host chain is an Arbitrum chain.
112
+ * That means that the block number returned for this is usable for event queries.
113
+ * This function will revert if the given node number does not exist.
114
+ * @dev This function is meant for internal use only and has no stability guarantees.
115
+ */
116
+ function getNodeCreationBlockForLogLookup(uint64 nodeNum)
117
+ external
118
+ view
119
+ override
120
+ returns (uint256)
121
+ {
122
+ if (_hostChainIsArbitrum) {
123
+ uint256 blockNum = _nodeCreatedAtArbSysBlock[nodeNum];
124
+ require(blockNum > 0, "NO_NODE");
125
+ return blockNum;
126
+ } else {
127
+ Node storage node = getNodeStorage(nodeNum);
128
+ require(node.deadlineBlock != 0, "NO_NODE");
129
+ return node.createdAtBlock;
130
+ }
131
+ }
132
+
95
133
  /**
96
134
  * @notice Check if the specified node has been staked on by the provided staker.
97
135
  * Only accurate at the latest confirmed node and afterwards.
@@ -250,6 +288,9 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
250
288
  __Pausable_init();
251
289
  _nodes[GENESIS_NODE] = initialNode;
252
290
  _firstUnresolvedNode = GENESIS_NODE + 1;
291
+ if (_hostChainIsArbitrum) {
292
+ _nodeCreatedAtArbSysBlock[GENESIS_NODE] = ArbSys(address(100)).arbBlockNumber();
293
+ }
253
294
  }
254
295
 
255
296
  /**
@@ -259,6 +300,9 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable {
259
300
  function nodeCreated(Node memory node) internal {
260
301
  _latestNodeCreated++;
261
302
  _nodes[_latestNodeCreated] = node;
303
+ if (_hostChainIsArbitrum) {
304
+ _nodeCreatedAtArbSysBlock[_latestNodeCreated] = ArbSys(address(100)).arbBlockNumber();
305
+ }
262
306
  }
263
307
 
264
308
  /// @notice Reject the next unresolved node
@@ -52,66 +52,55 @@ contract RollupCreator is Ownable {
52
52
  emit TemplatesUpdated();
53
53
  }
54
54
 
55
- struct CreateRollupFrame {
56
- ProxyAdmin admin;
57
- IBridge bridge;
58
- ISequencerInbox sequencerInbox;
59
- IInbox inbox;
60
- IRollupEventInbox rollupEventInbox;
61
- IOutbox outbox;
62
- RollupProxy rollup;
63
- }
64
-
65
55
  // After this setup:
66
56
  // Rollup should be the owner of bridge
67
57
  // RollupOwner should be the owner of Rollup's ProxyAdmin
68
58
  // RollupOwner should be the owner of Rollup
69
59
  // Bridge should have a single inbox and outbox
70
- function createRollup(Config memory config, address expectedRollupAddr)
71
- external
72
- returns (address)
73
- {
74
- CreateRollupFrame memory frame;
75
- frame.admin = new ProxyAdmin();
60
+ function createRollup(Config memory config) external returns (address) {
61
+ ProxyAdmin proxyAdmin = new ProxyAdmin();
62
+
63
+ // Create the rollup proxy to figure out the address and initialize it later
64
+ RollupProxy rollup = new RollupProxy{salt: keccak256(abi.encode(config))}();
76
65
 
77
66
  (
78
- frame.bridge,
79
- frame.sequencerInbox,
80
- frame.inbox,
81
- frame.rollupEventInbox,
82
- frame.outbox
67
+ IBridge bridge,
68
+ ISequencerInbox sequencerInbox,
69
+ IInbox inbox,
70
+ IRollupEventInbox rollupEventInbox,
71
+ IOutbox outbox
83
72
  ) = bridgeCreator.createBridge(
84
- address(frame.admin),
85
- expectedRollupAddr,
86
- config.sequencerInboxMaxTimeVariation
87
- );
73
+ address(proxyAdmin),
74
+ address(rollup),
75
+ config.sequencerInboxMaxTimeVariation
76
+ );
88
77
 
89
- frame.admin.transferOwnership(config.owner);
78
+ proxyAdmin.transferOwnership(config.owner);
90
79
 
91
80
  IChallengeManager challengeManager = IChallengeManager(
92
81
  address(
93
82
  new TransparentUpgradeableProxy(
94
83
  address(challengeManagerTemplate),
95
- address(frame.admin),
84
+ address(proxyAdmin),
96
85
  ""
97
86
  )
98
87
  )
99
88
  );
100
89
  challengeManager.initialize(
101
- IChallengeResultReceiver(expectedRollupAddr),
102
- frame.sequencerInbox,
103
- frame.bridge,
90
+ IChallengeResultReceiver(address(rollup)),
91
+ sequencerInbox,
92
+ bridge,
104
93
  osp
105
94
  );
106
95
 
107
- frame.rollup = new RollupProxy(
96
+ rollup.initializeProxy(
108
97
  config,
109
98
  ContractDependencies({
110
- bridge: frame.bridge,
111
- sequencerInbox: frame.sequencerInbox,
112
- inbox: frame.inbox,
113
- outbox: frame.outbox,
114
- rollupEventInbox: frame.rollupEventInbox,
99
+ bridge: bridge,
100
+ sequencerInbox: sequencerInbox,
101
+ inbox: inbox,
102
+ outbox: outbox,
103
+ rollupEventInbox: rollupEventInbox,
115
104
  challengeManager: challengeManager,
116
105
  rollupAdminLogic: rollupAdminLogic,
117
106
  rollupUserLogic: rollupUserLogic,
@@ -119,15 +108,14 @@ contract RollupCreator is Ownable {
119
108
  validatorWalletCreator: validatorWalletCreator
120
109
  })
121
110
  );
122
- require(address(frame.rollup) == expectedRollupAddr, "WRONG_ROLLUP_ADDR");
123
111
 
124
112
  emit RollupCreated(
125
- address(frame.rollup),
126
- address(frame.inbox),
127
- address(frame.admin),
128
- address(frame.sequencerInbox),
129
- address(frame.bridge)
113
+ address(rollup),
114
+ address(inbox),
115
+ address(proxyAdmin),
116
+ address(sequencerInbox),
117
+ address(bridge)
130
118
  );
131
- return address(frame.rollup);
119
+ return address(rollup);
132
120
  }
133
121
  }
@@ -30,8 +30,13 @@ contract RollupEventInbox is IRollupEventInbox, IDelayedMessageProvider, Delegat
30
30
  rollup = address(_bridge.rollup());
31
31
  }
32
32
 
33
- function rollupInitialized(uint256 chainId) external override onlyRollup {
34
- bytes memory initMsg = abi.encodePacked(chainId);
33
+ function rollupInitialized(uint256 chainId, string calldata chainConfig)
34
+ external
35
+ override
36
+ onlyRollup
37
+ {
38
+ require(bytes(chainConfig).length > 0, "EMPTY_CHAIN_CONFIG");
39
+ bytes memory initMsg = abi.encodePacked(chainId, uint8(0), chainConfig);
35
40
  uint256 num = bridge.enqueueDelayedMessage(
36
41
  INITIALIZATION_MSG_TYPE,
37
42
  address(0),
@@ -24,6 +24,7 @@ struct Config {
24
24
  address owner;
25
25
  address loserStakeEscrow;
26
26
  uint256 chainId;
27
+ string chainConfig;
27
28
  uint64 genesisBlockNum;
28
29
  ISequencerInbox.MaxTimeVariation sequencerInboxMaxTimeVariation;
29
30
  }
@@ -8,13 +8,27 @@ import "../libraries/AdminFallbackProxy.sol";
8
8
  import "./IRollupLogic.sol";
9
9
 
10
10
  contract RollupProxy is AdminFallbackProxy {
11
- constructor(Config memory config, ContractDependencies memory connectedContracts)
12
- AdminFallbackProxy(
13
- address(connectedContracts.rollupAdminLogic),
14
- abi.encodeWithSelector(IRollupAdmin.initialize.selector, config, connectedContracts),
15
- address(connectedContracts.rollupUserLogic),
16
- abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken),
17
- config.owner
18
- )
19
- {}
11
+ function initializeProxy(Config memory config, ContractDependencies memory connectedContracts)
12
+ external
13
+ {
14
+ if (
15
+ _getAdmin() == address(0) &&
16
+ _getImplementation() == address(0) &&
17
+ _getSecondaryImplementation() == address(0)
18
+ ) {
19
+ _initialize(
20
+ address(connectedContracts.rollupAdminLogic),
21
+ abi.encodeWithSelector(
22
+ IRollupAdmin.initialize.selector,
23
+ config,
24
+ connectedContracts
25
+ ),
26
+ address(connectedContracts.rollupUserLogic),
27
+ abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken),
28
+ config.owner
29
+ );
30
+ } else {
31
+ _fallback();
32
+ }
33
+ }
20
34
  }