diff --git a/contracts/docs/apis/L1ERC1155Gateway.md b/contracts/docs/apis/L1ERC1155Gateway.md index dd4bb05bc..663093fa7 100644 --- a/contracts/docs/apis/L1ERC1155Gateway.md +++ b/contracts/docs/apis/L1ERC1155Gateway.md @@ -366,6 +366,23 @@ Update layer 2 to layer 2 token mapping. | _l1Token | address | The address of corresponding ERC1155 token in layer 2. | | _l2Token | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/docs/apis/L1ERC721Gateway.md b/contracts/docs/apis/L1ERC721Gateway.md index 47d63a29d..3dc1a55fc 100644 --- a/contracts/docs/apis/L1ERC721Gateway.md +++ b/contracts/docs/apis/L1ERC721Gateway.md @@ -311,6 +311,23 @@ Update layer 2 to layer 2 token mapping. | _l1Token | address | The address of corresponding ERC721 token in layer 2. | | _l2Token | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/docs/apis/L1GatewayRouter.md b/contracts/docs/apis/L1GatewayRouter.md index a830c6574..fa5e11a1c 100644 --- a/contracts/docs/apis/L1GatewayRouter.md +++ b/contracts/docs/apis/L1GatewayRouter.md @@ -380,6 +380,23 @@ function transferOwnership(address newOwner) external nonpayable |---|---|---| | newOwner | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/docs/apis/L1ScrollMessenger.md b/contracts/docs/apis/L1ScrollMessenger.md index 19a7a0ed3..9719ae873 100644 --- a/contracts/docs/apis/L1ScrollMessenger.md +++ b/contracts/docs/apis/L1ScrollMessenger.md @@ -369,6 +369,23 @@ Update whitelist contract. |---|---|---| | _newWhitelist | address | The address of new whitelist contract. | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### whitelist ```solidity diff --git a/contracts/docs/apis/L1StandardERC20Gateway.md b/contracts/docs/apis/L1StandardERC20Gateway.md index 73fb6185b..9b6abd97f 100644 --- a/contracts/docs/apis/L1StandardERC20Gateway.md +++ b/contracts/docs/apis/L1StandardERC20Gateway.md @@ -226,6 +226,23 @@ The address of L1GatewayRouter/L2GatewayRouter contract. |---|---|---| | _0 | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/docs/apis/L1WETHGateway.md b/contracts/docs/apis/L1WETHGateway.md index 77bb6a983..f91c32a00 100644 --- a/contracts/docs/apis/L1WETHGateway.md +++ b/contracts/docs/apis/L1WETHGateway.md @@ -226,6 +226,23 @@ The address of L1GatewayRouter/L2GatewayRouter contract. |---|---|---| | _0 | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/docs/apis/L2ERC1155Gateway.md b/contracts/docs/apis/L2ERC1155Gateway.md index 81349c52e..ad1065780 100644 --- a/contracts/docs/apis/L2ERC1155Gateway.md +++ b/contracts/docs/apis/L2ERC1155Gateway.md @@ -327,6 +327,23 @@ Update layer 2 to layer 1 token mapping. | _l2Token | address | undefined | | _l1Token | address | The address of ERC1155 token in layer 1. | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### withdrawERC1155 ```solidity diff --git a/contracts/docs/apis/L2ERC721Gateway.md b/contracts/docs/apis/L2ERC721Gateway.md index 8d9bd1965..1f5d57ee8 100644 --- a/contracts/docs/apis/L2ERC721Gateway.md +++ b/contracts/docs/apis/L2ERC721Gateway.md @@ -274,6 +274,23 @@ Update layer 2 to layer 1 token mapping. | _l2Token | address | undefined | | _l1Token | address | The address of ERC721 token in layer 1. | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### withdrawERC721 ```solidity diff --git a/contracts/docs/apis/L2GatewayRouter.md b/contracts/docs/apis/L2GatewayRouter.md index 45ac64ff2..0edfa6d5c 100644 --- a/contracts/docs/apis/L2GatewayRouter.md +++ b/contracts/docs/apis/L2GatewayRouter.md @@ -312,6 +312,23 @@ function transferOwnership(address newOwner) external nonpayable |---|---|---| | newOwner | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### withdrawERC20 ```solidity diff --git a/contracts/docs/apis/L2ScrollMessenger.md b/contracts/docs/apis/L2ScrollMessenger.md index 4c920beb5..b10a6213c 100644 --- a/contracts/docs/apis/L2ScrollMessenger.md +++ b/contracts/docs/apis/L2ScrollMessenger.md @@ -295,6 +295,23 @@ Update whitelist contract. |---|---|---| | _newWhitelist | address | The address of new whitelist contract. | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### whitelist ```solidity diff --git a/contracts/docs/apis/L2StandardERC20Gateway.md b/contracts/docs/apis/L2StandardERC20Gateway.md index a6185aae1..be3a150f3 100644 --- a/contracts/docs/apis/L2StandardERC20Gateway.md +++ b/contracts/docs/apis/L2StandardERC20Gateway.md @@ -173,6 +173,23 @@ The address of ScrollStandardERC20Factory. |---|---|---| | _0 | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### withdrawERC20 ```solidity diff --git a/contracts/docs/apis/L2WETHGateway.md b/contracts/docs/apis/L2WETHGateway.md index 025b13a61..575ff2f8c 100644 --- a/contracts/docs/apis/L2WETHGateway.md +++ b/contracts/docs/apis/L2WETHGateway.md @@ -191,6 +191,23 @@ The address of L1GatewayRouter/L2GatewayRouter contract. |---|---|---| | _0 | address | undefined | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ### withdrawERC20 ```solidity diff --git a/contracts/docs/apis/ZKRollup.md b/contracts/docs/apis/ZKRollup.md index cbcc4da77..73a66ac51 100644 --- a/contracts/docs/apis/ZKRollup.md +++ b/contracts/docs/apis/ZKRollup.md @@ -364,6 +364,23 @@ Update the address of operator. |---|---|---| | _newOperator | address | The new operator address to update. | +### version + +```solidity +function version() external view returns (string) +``` + +the current contract version. + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | string | undefined | + ## Events diff --git a/contracts/integration-test/L1BlockContainer.spec.ts b/contracts/integration-test/L1BlockContainer.spec.ts index f9a4094c8..0f17d00f4 100644 --- a/contracts/integration-test/L1BlockContainer.spec.ts +++ b/contracts/integration-test/L1BlockContainer.spec.ts @@ -129,6 +129,7 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight - 1, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); const Whitelist = await ethers.getContractFactory("Whitelist", deployer); @@ -144,6 +145,7 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight - 1, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); const headerRLP = encodeHeader(test); @@ -158,6 +160,7 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight - 1, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); const headerRLP = encodeHeader(test); @@ -172,6 +175,7 @@ describe("L1BlockContainer", async () => { constants.HashZero, test.blockHeight - 1, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); const headerRLP = encodeHeader(test); @@ -184,6 +188,7 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); const headerRLP = encodeHeader(test); @@ -196,6 +201,7 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight - 1, test.blockTimestamp + 1, + test.baseFee, test.stateRoot ); const headerRLP = encodeHeader(test); @@ -210,16 +216,20 @@ describe("L1BlockContainer", async () => { test.parentHash, test.blockHeight - 1, test.blockTimestamp - 1, + test.baseFee, test.stateRoot ); expect(await container.latestBlockHash()).to.eq(test.parentHash); const headerRLP = encodeHeader(test); await expect(container.importBlockHeader(test.hash, headerRLP, "0x")) .to.emit(container, "ImportBlock") - .withArgs(test.hash, test.blockHeight, test.blockTimestamp, test.stateRoot); + .withArgs(test.hash, test.blockHeight, test.blockTimestamp, test.baseFee, test.stateRoot); expect(await container.getStateRoot(test.hash)).to.eq(test.stateRoot); expect(await container.getBlockTimestamp(test.hash)).to.eq(test.blockTimestamp); expect(await container.latestBlockHash()).to.eq(test.hash); + expect(await container.latestBaseFee()).to.eq(test.baseFee); + expect(await container.latestBlockNumber()).to.eq(test.blockHeight); + expect(await container.latestBlockTimestamp()).to.eq(test.blockTimestamp); }); }); } @@ -238,7 +248,14 @@ describe("L1BlockContainer", async () => { await container.deployed(); const block = await ethers.provider.getBlock("latest"); - await container.initialize(deployer.address, block.hash, block.number, block.timestamp, block.hash); + await container.initialize( + deployer.address, + block.hash, + block.number, + block.timestamp, + block.baseFeePerGas!, + block.hash + ); }); it("should revert, when block not imported", async () => { diff --git a/contracts/src/L1/L1ScrollMessenger.sol b/contracts/src/L1/L1ScrollMessenger.sol index a2db4b326..f14526b9c 100644 --- a/contracts/src/L1/L1ScrollMessenger.sol +++ b/contracts/src/L1/L1ScrollMessenger.sol @@ -8,6 +8,7 @@ import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/securit import { IZKRollup } from "./rollup/IZKRollup.sol"; import { L1MessageQueue } from "./rollup/L1MessageQueue.sol"; import { IL1ScrollMessenger, IScrollMessenger } from "./IL1ScrollMessenger.sol"; +import { Version } from "../libraries/common/Version.sol"; import { IGasOracle } from "../libraries/oracle/IGasOracle.sol"; import { ScrollConstants } from "../libraries/ScrollConstants.sol"; import { ScrollMessengerBase } from "../libraries/ScrollMessengerBase.sol"; @@ -23,7 +24,7 @@ import { ZkTrieVerifier } from "../libraries/verifier/ZkTrieVerifier.sol"; /// /// @dev All deposited Ether (including `WETH` deposited throng `L1WETHGateway`) will locked in /// this contract. -contract L1ScrollMessenger is OwnableUpgradeable, PausableUpgradeable, ScrollMessengerBase, IL1ScrollMessenger { +contract L1ScrollMessenger is Version, OwnableUpgradeable, PausableUpgradeable, ScrollMessengerBase, IL1ScrollMessenger { /************* * Variables * *************/ diff --git a/contracts/src/L1/gateways/L1CustomERC20Gateway.sol b/contracts/src/L1/gateways/L1CustomERC20Gateway.sol index fdfbe62f5..f8ba7834f 100644 --- a/contracts/src/L1/gateways/L1CustomERC20Gateway.sol +++ b/contracts/src/L1/gateways/L1CustomERC20Gateway.sol @@ -9,6 +9,7 @@ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ import { IL1ERC20Gateway, L1ERC20Gateway } from "./L1ERC20Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IL2ERC20Gateway } from "../../L2/gateways/IL2ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1CustomERC20Gateway @@ -16,7 +17,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// finalize withdraw the tokens from layer 2. /// @dev The deposited tokens are held in this gateway. On finalizing withdraw, the corresponding /// tokens will be transfer to the recipient directly. -contract L1CustomERC20Gateway is OwnableUpgradeable, ScrollGatewayBase, L1ERC20Gateway { +contract L1CustomERC20Gateway is Version, OwnableUpgradeable, ScrollGatewayBase, L1ERC20Gateway { using SafeERC20Upgradeable for IERC20Upgradeable; /**************************************** Events ****************************************/ diff --git a/contracts/src/L1/gateways/L1ERC1155Gateway.sol b/contracts/src/L1/gateways/L1ERC1155Gateway.sol index 3aa50b881..574254d81 100644 --- a/contracts/src/L1/gateways/L1ERC1155Gateway.sol +++ b/contracts/src/L1/gateways/L1ERC1155Gateway.sol @@ -9,6 +9,7 @@ import { ERC1155HolderUpgradeable } from "@openzeppelin/contracts-upgradeable/to import { IL1ERC1155Gateway } from "./IL1ERC1155Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IL2ERC1155Gateway } from "../../L2/gateways/IL2ERC1155Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1ERC1155Gateway @@ -19,7 +20,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// /// This will be changed if we have more specific scenarios. // @todo Current implementation doesn't support calling from `L1GatewayRouter`. -contract L1ERC1155Gateway is OwnableUpgradeable, ERC1155HolderUpgradeable, ScrollGatewayBase, IL1ERC1155Gateway { +contract L1ERC1155Gateway is Version, OwnableUpgradeable, ERC1155HolderUpgradeable, ScrollGatewayBase, IL1ERC1155Gateway { /**************************************** Events ****************************************/ /// @notice Emitted when token mapping for ERC1155 token is updated. diff --git a/contracts/src/L1/gateways/L1ERC20Gateway.sol b/contracts/src/L1/gateways/L1ERC20Gateway.sol index 898938ae0..bc8322b42 100644 --- a/contracts/src/L1/gateways/L1ERC20Gateway.sol +++ b/contracts/src/L1/gateways/L1ERC20Gateway.sol @@ -2,11 +2,13 @@ pragma solidity ^0.8.0; +import { Version } from "../../libraries/common/Version.sol"; + import { IL1ERC20Gateway } from "./IL1ERC20Gateway.sol"; // solhint-disable no-empty-blocks -abstract contract L1ERC20Gateway is IL1ERC20Gateway { +abstract contract L1ERC20Gateway is Version, IL1ERC20Gateway { /// @inheritdoc IL1ERC20Gateway function depositERC20( address _token, diff --git a/contracts/src/L1/gateways/L1ERC721Gateway.sol b/contracts/src/L1/gateways/L1ERC721Gateway.sol index 5e302a9c8..fd0bf9d76 100644 --- a/contracts/src/L1/gateways/L1ERC721Gateway.sol +++ b/contracts/src/L1/gateways/L1ERC721Gateway.sol @@ -9,6 +9,7 @@ import { ERC721HolderUpgradeable } from "@openzeppelin/contracts-upgradeable/tok import { IL1ERC721Gateway } from "./IL1ERC721Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IL2ERC721Gateway } from "../../L2/gateways/IL2ERC721Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1ERC721Gateway @@ -19,7 +20,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// /// This will be changed if we have more specific scenarios. // @todo Current implementation doesn't support calling from `L1GatewayRouter`. -contract L1ERC721Gateway is OwnableUpgradeable, ERC721HolderUpgradeable, ScrollGatewayBase, IL1ERC721Gateway { +contract L1ERC721Gateway is Version, OwnableUpgradeable, ERC721HolderUpgradeable, ScrollGatewayBase, IL1ERC721Gateway { /**************************************** Events ****************************************/ /// @notice Emitted when token mapping for ERC721 token is updated. diff --git a/contracts/src/L1/gateways/L1GatewayRouter.sol b/contracts/src/L1/gateways/L1GatewayRouter.sol index 27d790676..fa55d7e17 100644 --- a/contracts/src/L1/gateways/L1GatewayRouter.sol +++ b/contracts/src/L1/gateways/L1GatewayRouter.sol @@ -8,6 +8,7 @@ import { IL1GatewayRouter } from "./IL1GatewayRouter.sol"; import { IL1ERC20Gateway } from "./IL1ERC20Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IL2GatewayRouter } from "../../L2/gateways/IL2GatewayRouter.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { IScrollGateway } from "../../libraries/gateway/IScrollGateway.sol"; import { ScrollGatewayBase } from "../../libraries/gateway/ScrollGatewayBase.sol"; @@ -16,7 +17,7 @@ import { ScrollGatewayBase } from "../../libraries/gateway/ScrollGatewayBase.sol /// All deposited tokens are routed to corresponding gateways. /// @dev One can also use this contract to query L1/L2 token address mapping. /// In the future, ERC-721 and ERC-1155 tokens will be added to the router too. -contract L1GatewayRouter is OwnableUpgradeable, ScrollGatewayBase, IL1GatewayRouter { +contract L1GatewayRouter is Version, OwnableUpgradeable, ScrollGatewayBase, IL1GatewayRouter { /**************************************** Events ****************************************/ event SetDefaultERC20Gateway(address indexed _defaultERC20Gateway); diff --git a/contracts/src/L1/gateways/L1StandardERC20Gateway.sol b/contracts/src/L1/gateways/L1StandardERC20Gateway.sol index 59f7b0464..ea5315213 100644 --- a/contracts/src/L1/gateways/L1StandardERC20Gateway.sol +++ b/contracts/src/L1/gateways/L1StandardERC20Gateway.sol @@ -11,6 +11,7 @@ import { L1ERC20Gateway, IL1ERC20Gateway } from "./L1ERC20Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IERC20Metadata } from "../../interfaces/IERC20Metadata.sol"; import { IL2ERC20Gateway } from "../../L2/gateways/IL2ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1StandardERC20Gateway @@ -19,7 +20,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// @dev The deposited ERC20 tokens are held in this gateway. On finalizing withdraw, the corresponding /// token will be transfer to the recipient directly. Any ERC20 that requires non-standard functionality /// should use a separate gateway. -contract L1StandardERC20Gateway is Initializable, ScrollGatewayBase, L1ERC20Gateway { +contract L1StandardERC20Gateway is Version, Initializable, ScrollGatewayBase, L1ERC20Gateway { using SafeERC20 for IERC20; /**************************************** Variables ****************************************/ diff --git a/contracts/src/L1/gateways/L1WETHGateway.sol b/contracts/src/L1/gateways/L1WETHGateway.sol index 199f3523b..0a3494850 100644 --- a/contracts/src/L1/gateways/L1WETHGateway.sol +++ b/contracts/src/L1/gateways/L1WETHGateway.sol @@ -10,6 +10,7 @@ import { L1ERC20Gateway, IL1ERC20Gateway } from "./L1ERC20Gateway.sol"; import { IL1ScrollMessenger } from "../IL1ScrollMessenger.sol"; import { IWETH } from "../../interfaces/IWETH.sol"; import { IL2ERC20Gateway } from "../../L2/gateways/IL2ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1WETHGateway @@ -19,7 +20,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// as Ether and then the Ether will be sent to the `L1ScrollMessenger` contract. /// On finalizing withdraw, the Ether will be transfered from `L1ScrollMessenger`, then /// wrapped as WETH and finally transfer to recipient. -contract L1WETHGateway is Initializable, ScrollGatewayBase, L1ERC20Gateway { +contract L1WETHGateway is Version, Initializable, ScrollGatewayBase, L1ERC20Gateway { using SafeERC20 for IERC20; /**************************************** Variables ****************************************/ diff --git a/contracts/src/L1/rollup/L1MessageQueue.sol b/contracts/src/L1/rollup/L1MessageQueue.sol index bfa8e0e09..c4ffcda7c 100644 --- a/contracts/src/L1/rollup/L1MessageQueue.sol +++ b/contracts/src/L1/rollup/L1MessageQueue.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.0; +import { Version } from "../../libraries/common/Version.sol"; import { IL1MessageQueue } from "./IL1MessageQueue.sol"; /// @title L1MessageQueue /// @notice This contract will hold all L1 to L2 messages. /// Each appended message is assigned with a unique and increasing `uint256` index denoting the message nonce. -contract L1MessageQueue is IL1MessageQueue { +contract L1MessageQueue is Version, IL1MessageQueue { /************* * Constants * *************/ diff --git a/contracts/src/L1/rollup/ZKRollup.sol b/contracts/src/L1/rollup/ZKRollup.sol index 6fd4fc1bb..5c1bb07ea 100644 --- a/contracts/src/L1/rollup/ZKRollup.sol +++ b/contracts/src/L1/rollup/ZKRollup.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.0; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { IZKRollup } from "./IZKRollup.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { RollupVerifier } from "../../libraries/verifier/RollupVerifier.sol"; // solhint-disable reason-string @@ -16,7 +17,7 @@ import { RollupVerifier } from "../../libraries/verifier/RollupVerifier.sol"; /// 2. the block tree generated by layer 2 and it's status. /// /// @dev the message queue is not used yet, the offline relayer only use events in `L1ScrollMessenger`. -contract ZKRollup is OwnableUpgradeable, IZKRollup { +contract ZKRollup is Version, OwnableUpgradeable, IZKRollup { /********** * Events * **********/ diff --git a/contracts/src/L2/L2ScrollMessenger.sol b/contracts/src/L2/L2ScrollMessenger.sol index dcd8ab4ba..92df0a83c 100644 --- a/contracts/src/L2/L2ScrollMessenger.sol +++ b/contracts/src/L2/L2ScrollMessenger.sol @@ -6,6 +6,7 @@ import { IL2ScrollMessenger, IScrollMessenger } from "./IL2ScrollMessenger.sol"; import { L2MessageQueue } from "./predeploys/L2MessageQueue.sol"; import { IL1BlockContainer } from "./predeploys/IL1BlockContainer.sol"; import { OwnableBase } from "../libraries/common/OwnableBase.sol"; +import { Version } from "../libraries/common/Version.sol"; import { IGasOracle } from "../libraries/oracle/IGasOracle.sol"; import { ScrollConstants } from "../libraries/ScrollConstants.sol"; import { ScrollMessengerBase } from "../libraries/ScrollMessengerBase.sol"; @@ -19,7 +20,7 @@ import { ScrollMessengerBase } from "../libraries/ScrollMessengerBase.sol"; /// /// @dev It should be a predeployed contract in layer 2 and should hold infinite amount /// of Ether (Specifically, `uint256(-1)`), which can be initialized in Genesis Block. -contract L2ScrollMessenger is ScrollMessengerBase, OwnableBase, IL2ScrollMessenger { +contract L2ScrollMessenger is Version, ScrollMessengerBase, OwnableBase, IL2ScrollMessenger { /**************************************** Variables ****************************************/ /// @notice Mapping from relay id to relay status. @@ -36,7 +37,7 @@ contract L2ScrollMessenger is ScrollMessengerBase, OwnableBase, IL2ScrollMesseng constructor(address _owner) { ScrollMessengerBase._initialize(); - owner = _owner; + _transferOwnership(_owner); // initialize to a nonzero value xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; diff --git a/contracts/src/L2/gateways/L2CustomERC20Gateway.sol b/contracts/src/L2/gateways/L2CustomERC20Gateway.sol index a2ba1fa52..086e5b29f 100644 --- a/contracts/src/L2/gateways/L2CustomERC20Gateway.sol +++ b/contracts/src/L2/gateways/L2CustomERC20Gateway.sol @@ -8,6 +8,7 @@ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC import { IL2ERC20Gateway, L2ERC20Gateway } from "./L2ERC20Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IL1ERC20Gateway } from "../../L1/gateways/IL1ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; import { IScrollStandardERC20 } from "../../libraries/token/IScrollStandardERC20.sol"; @@ -16,7 +17,7 @@ import { IScrollStandardERC20 } from "../../libraries/token/IScrollStandardERC20 /// finalize deposit the tokens from layer 1. /// @dev The withdrawn tokens tokens will be burned directly. On finalizing deposit, the corresponding /// tokens will be minted and transfered to the recipient. -contract L2CustomERC20Gateway is OwnableUpgradeable, ScrollGatewayBase, L2ERC20Gateway { +contract L2CustomERC20Gateway is Version, OwnableUpgradeable, ScrollGatewayBase, L2ERC20Gateway { /**************************************** Events ****************************************/ /// @notice Emitted when token mapping for ERC20 token is updated. diff --git a/contracts/src/L2/gateways/L2ERC1155Gateway.sol b/contracts/src/L2/gateways/L2ERC1155Gateway.sol index a7343568a..4eca63501 100644 --- a/contracts/src/L2/gateways/L2ERC1155Gateway.sol +++ b/contracts/src/L2/gateways/L2ERC1155Gateway.sol @@ -9,6 +9,7 @@ import { ERC1155HolderUpgradeable } from "@openzeppelin/contracts-upgradeable/to import { IL2ERC1155Gateway } from "./IL2ERC1155Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IL1ERC1155Gateway } from "../../L1/gateways/IL1ERC1155Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; import { IScrollERC1155 } from "../../libraries/token/IScrollERC1155.sol"; @@ -20,7 +21,7 @@ import { IScrollERC1155 } from "../../libraries/token/IScrollERC1155.sol"; /// /// This will be changed if we have more specific scenarios. // @todo Current implementation doesn't support calling from `L2GatewayRouter`. -contract L2ERC1155Gateway is OwnableUpgradeable, ERC1155HolderUpgradeable, ScrollGatewayBase, IL2ERC1155Gateway { +contract L2ERC1155Gateway is Version, OwnableUpgradeable, ERC1155HolderUpgradeable, ScrollGatewayBase, IL2ERC1155Gateway { /**************************************** Events ****************************************/ /// @notice Emitted when token mapping for ERC1155 token is updated. diff --git a/contracts/src/L2/gateways/L2ERC20Gateway.sol b/contracts/src/L2/gateways/L2ERC20Gateway.sol index 535efb6d6..b838e60f0 100644 --- a/contracts/src/L2/gateways/L2ERC20Gateway.sol +++ b/contracts/src/L2/gateways/L2ERC20Gateway.sol @@ -3,10 +3,11 @@ pragma solidity ^0.8.0; import { IL2ERC20Gateway } from "./IL2ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; // solhint-disable no-empty-blocks -abstract contract L2ERC20Gateway is IL2ERC20Gateway { +abstract contract L2ERC20Gateway is Version, IL2ERC20Gateway { /// @inheritdoc IL2ERC20Gateway function withdrawERC20( address _token, diff --git a/contracts/src/L2/gateways/L2ERC721Gateway.sol b/contracts/src/L2/gateways/L2ERC721Gateway.sol index 297965e18..75bb44eaf 100644 --- a/contracts/src/L2/gateways/L2ERC721Gateway.sol +++ b/contracts/src/L2/gateways/L2ERC721Gateway.sol @@ -9,6 +9,7 @@ import { ERC721HolderUpgradeable } from "@openzeppelin/contracts-upgradeable/tok import { IL2ERC721Gateway } from "./IL2ERC721Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IL1ERC721Gateway } from "../../L1/gateways/IL1ERC721Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; import { IScrollERC721 } from "../../libraries/token/IScrollERC721.sol"; @@ -20,7 +21,7 @@ import { IScrollERC721 } from "../../libraries/token/IScrollERC721.sol"; /// /// This will be changed if we have more specific scenarios. // @todo Current implementation doesn't support calling from `L2GatewayRouter`. -contract L2ERC721Gateway is OwnableUpgradeable, ERC721HolderUpgradeable, ScrollGatewayBase, IL2ERC721Gateway { +contract L2ERC721Gateway is Version, OwnableUpgradeable, ERC721HolderUpgradeable, ScrollGatewayBase, IL2ERC721Gateway { /**************************************** Events ****************************************/ /// @notice Emitted when token mapping for ERC721 token is updated. diff --git a/contracts/src/L2/gateways/L2GatewayRouter.sol b/contracts/src/L2/gateways/L2GatewayRouter.sol index 29cd95501..b5ad437fe 100644 --- a/contracts/src/L2/gateways/L2GatewayRouter.sol +++ b/contracts/src/L2/gateways/L2GatewayRouter.sol @@ -8,6 +8,7 @@ import { IL2GatewayRouter } from "./IL2GatewayRouter.sol"; import { IL2ERC20Gateway } from "./IL2ERC20Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IL1GatewayRouter } from "../../L1/gateways/IL1GatewayRouter.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { IScrollGateway } from "../../libraries/gateway/IScrollGateway.sol"; import { ScrollGatewayBase } from "../../libraries/gateway/ScrollGatewayBase.sol"; import { IScrollStandardERC20 } from "../../libraries/token/IScrollStandardERC20.sol"; @@ -17,7 +18,7 @@ import { IScrollStandardERC20 } from "../../libraries/token/IScrollStandardERC20 /// All deposited tokens are routed to corresponding gateways. /// @dev One can also use this contract to query L1/L2 token address mapping. /// In the future, ERC-721 and ERC-1155 tokens will be added to the router too. -contract L2GatewayRouter is OwnableUpgradeable, ScrollGatewayBase, IL2GatewayRouter { +contract L2GatewayRouter is Version, OwnableUpgradeable, ScrollGatewayBase, IL2GatewayRouter { /**************************************** Events ****************************************/ event SetDefaultERC20Gateway(address indexed _defaultERC20Gateway); diff --git a/contracts/src/L2/gateways/L2StandardERC20Gateway.sol b/contracts/src/L2/gateways/L2StandardERC20Gateway.sol index 76aa596a3..b8ef18f88 100644 --- a/contracts/src/L2/gateways/L2StandardERC20Gateway.sol +++ b/contracts/src/L2/gateways/L2StandardERC20Gateway.sol @@ -10,6 +10,7 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IL2ERC20Gateway, L2ERC20Gateway } from "./L2ERC20Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IL1ERC20Gateway } from "../../L1/gateways/IL1ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { IScrollStandardERC20 } from "../../libraries/token/IScrollStandardERC20.sol"; import { ScrollStandardERC20 } from "../../libraries/token/ScrollStandardERC20.sol"; import { IScrollStandardERC20Factory } from "../../libraries/token/IScrollStandardERC20Factory.sol"; @@ -21,7 +22,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// @dev The withdrawn ERC20 tokens will be burned directly. On finalizing deposit, the corresponding /// token will be minted and transfered to the recipient. Any ERC20 that requires non-standard functionality /// should use a separate gateway. -contract L2StandardERC20Gateway is Initializable, ScrollGatewayBase, L2ERC20Gateway { +contract L2StandardERC20Gateway is Version, Initializable, ScrollGatewayBase, L2ERC20Gateway { using SafeERC20 for IERC20; using Address for address; diff --git a/contracts/src/L2/gateways/L2WETHGateway.sol b/contracts/src/L2/gateways/L2WETHGateway.sol index 23d9348ae..cacd4656b 100644 --- a/contracts/src/L2/gateways/L2WETHGateway.sol +++ b/contracts/src/L2/gateways/L2WETHGateway.sol @@ -10,6 +10,7 @@ import { IL2ERC20Gateway, L2ERC20Gateway } from "./L2ERC20Gateway.sol"; import { IL2ScrollMessenger } from "../IL2ScrollMessenger.sol"; import { IWETH } from "../../interfaces/IWETH.sol"; import { IL1ERC20Gateway } from "../../L1/gateways/IL1ERC20Gateway.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L2WETHGateway @@ -19,7 +20,7 @@ import { ScrollGatewayBase, IScrollGateway } from "../../libraries/gateway/Scrol /// then the Ether will be sent to the `L2ScrollMessenger` contract. /// On finalizing deposit, the Ether will be transfered from `L2ScrollMessenger`, then /// wrapped as WETH and finally transfer to recipient. -contract L2WETHGateway is Initializable, ScrollGatewayBase, L2ERC20Gateway { +contract L2WETHGateway is Version, Initializable, ScrollGatewayBase, L2ERC20Gateway { using SafeERC20 for IERC20; /**************************************** Variables ****************************************/ diff --git a/contracts/src/L2/predeploys/IL1BlockContainer.sol b/contracts/src/L2/predeploys/IL1BlockContainer.sol index c2b9d7bb0..3b46b8409 100644 --- a/contracts/src/L2/predeploys/IL1BlockContainer.sol +++ b/contracts/src/L2/predeploys/IL1BlockContainer.sol @@ -11,27 +11,26 @@ interface IL1BlockContainer { /// @param blockHash The hash of the imported block. /// @param blockHeight The height of the imported block. /// @param blockTimestamp The timestamp of the imported block. + /// @param baseFee The base fee of the imported block. /// @param stateRoot The state root of the imported block. - event ImportBlock(bytes32 indexed blockHash, uint256 blockHeight, uint256 blockTimestamp, bytes32 stateRoot); - - /*********** - * Structs * - ***********/ - - /// @dev Compiler will pack this into single `uint256`. - struct BlockMetadata { - // The block height. - uint64 height; - // The block timestamp. - uint64 timestamp; - // The reserved field. - uint128 reserved; - } + event ImportBlock(bytes32 indexed blockHash, uint256 blockHeight, uint256 blockTimestamp, uint256 baseFee, bytes32 stateRoot); /************************* * Public View Functions * *************************/ + /// @notice Return the latest imported block hash + function latestBlockHash() external view returns (bytes32); + + /// @notice Return the latest imported L1 base fee + function latestBaseFee() external view returns (uint256); + + /// @notice Return the latest imported block number + function latestBlockNumber() external view returns (uint256); + + /// @notice Return the latest imported block timestamp + function latestBlockTimestamp() external view returns (uint256); + /// @notice Check whether the message is included in the corresponding L1 block. /// @param blockHash The block hash where the message should in. /// @param msgHash The hash of the message to check. diff --git a/contracts/src/L2/predeploys/IL2GasPriceOracle.sol b/contracts/src/L2/predeploys/IL2GasPriceOracle.sol new file mode 100644 index 000000000..dc3d65d91 --- /dev/null +++ b/contracts/src/L2/predeploys/IL2GasPriceOracle.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IL2GasPriceOracle { + /********** + * Events * + **********/ + + /// @notice Emitted when current fee overhead is updated. + /// @param overhead The current fee overhead updated. + event OverheadUpdated(uint256 overhead); + + /// @notice Emitted when current fee scalar is updated. + /// @param scalar The current fee scalar updated. + event ScalarUpdated(uint256 scalar); + + /************************* + * Public View Functions * + *************************/ + + /// @notice Return the current L2 base fee. + function baseFee() external view returns (uint256); + + /// @notice Return the current L2 gas price (base fee). + function gasPrice() external view returns (uint256); + + /// @notice Return the current l1 fee overhead. + function overhead() external view returns (uint256); + + /// @notice Return the current l1 fee scalar. + function scalar() external view returns (uint256); + + /// @notice Return the latest known l1 base fee. + function l1BaseFee() external view returns (uint256); + + /// @notice Computes the L1 portion of the fee based on the size of the rlp encoded input + /// transaction, the current L1 base fee, and the various dynamic parameters. + /// @param data Unsigned fully RLP-encoded transaction to get the L1 fee for. + /// @return L1 fee that should be paid for the tx + function getL1Fee(bytes memory data) external view returns (uint256); + + /// @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which + /// represents the per-transaction gas overhead of posting the transaction and state + /// roots to L1. Adds 68 bytes of padding to account for the fact that the input does + /// not have a signature. + /// @param data Unsigned fully RLP-encoded transaction to get the L1 gas for. + /// @return Amount of L1 gas used to publish the transaction. + function getL1GasUsed(bytes memory data) external view returns (uint256); +} diff --git a/contracts/src/L2/predeploys/L1BlockContainer.sol b/contracts/src/L2/predeploys/L1BlockContainer.sol index 1e07e0d02..b84bca337 100644 --- a/contracts/src/L2/predeploys/L1BlockContainer.sol +++ b/contracts/src/L2/predeploys/L1BlockContainer.sol @@ -8,11 +8,12 @@ import { IL1BlockContainer } from "./IL1BlockContainer.sol"; import { OwnableBase } from "../../libraries/common/OwnableBase.sol"; import { IWhitelist } from "../../libraries/common/IWhitelist.sol"; +import { Version } from "../../libraries/common/Version.sol"; import { PatriciaMerkleTrieVerifier } from "../../libraries/verifier/PatriciaMerkleTrieVerifier.sol"; /// @title L1BlockContainer /// @notice This contract will maintain the list of blocks proposed in L1. -contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { +contract L1BlockContainer is Version, Initializable, OwnableBase, IL1BlockContainer { /********** * Events * **********/ @@ -32,6 +33,20 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { /// @notice The address of L1ScrollMessenger contract. address public immutable messenger; + /*********** + * Structs * + ***********/ + + /// @dev Compiler will pack this into single `uint256`. + struct BlockMetadata { + // The block height. + uint64 height; + // The block timestamp. + uint64 timestamp; + // The base fee in the block. + uint128 baseFee; + } + /************* * Variables * *************/ @@ -41,8 +56,8 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { // @todo change to ring buffer to save gas usage. - /// @notice The hash of last imported block. - bytes32 public latestBlockHash; + /// @inheritdoc IL1BlockContainer + bytes32 public override latestBlockHash; /// @notice Mapping from block hash to corresponding state root. mapping(bytes32 => bytes32) public stateRoot; @@ -65,21 +80,37 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { bytes32 _startBlockHash, uint64 _startBlockHeight, uint64 _startBlockTimestamp, + uint128 _startBlockBaseFee, bytes32 _startStateRoot ) public initializer { - owner = _owner; + _transferOwnership(_owner); latestBlockHash = _startBlockHash; stateRoot[_startBlockHash] = _startStateRoot; - metadata[_startBlockHash] = BlockMetadata(_startBlockHeight, _startBlockTimestamp, 0); + metadata[_startBlockHash] = BlockMetadata(_startBlockHeight, _startBlockTimestamp, _startBlockBaseFee); - emit ImportBlock(_startBlockHash, _startBlockHeight, _startBlockTimestamp, _startStateRoot); + emit ImportBlock(_startBlockHash, _startBlockHeight, _startBlockTimestamp, _startBlockBaseFee, _startStateRoot); } /************************* * Public View Functions * *************************/ + /// @inheritdoc IL1BlockContainer + function latestBaseFee() external override view returns (uint256) { + return metadata[latestBlockHash].baseFee; + } + + /// @inheritdoc IL1BlockContainer + function latestBlockNumber() external override view returns (uint256) { + return metadata[latestBlockHash].height; + } + + /// @inheritdoc IL1BlockContainer + function latestBlockTimestamp() external override view returns (uint256) { + return metadata[latestBlockHash].timestamp; + } + /// @inheritdoc IL1BlockContainer function verifyMessageInclusionStatus( bytes32 _blockHash, @@ -181,6 +212,7 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { bytes32 _stateRoot; uint64 _height; uint64 _timestamp; + uint128 _baseFee; assembly { // reverts with error `msg`. @@ -304,6 +336,8 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { _height := mload(add(memPtr, 0x100)) // load block timestamp, 12-th entry _timestamp := mload(add(memPtr, 0x160)) + // load base fee, 16-th entry + _baseFee := mload(add(memPtr, 0x1e0)) } require(stateRoot[_parentHash] != bytes32(0), "Parent not imported"); BlockMetadata memory _parentMetadata = metadata[_parentHash]; @@ -312,9 +346,9 @@ contract L1BlockContainer is Initializable, OwnableBase, IL1BlockContainer { latestBlockHash = _blockHash; stateRoot[_blockHash] = _stateRoot; - metadata[_blockHash] = BlockMetadata(_height, _timestamp, 0); + metadata[_blockHash] = BlockMetadata(_height, _timestamp, _baseFee); - emit ImportBlock(_blockHash, _height, _timestamp, _stateRoot); + emit ImportBlock(_blockHash, _height, _timestamp, _baseFee, _stateRoot); } /************************ diff --git a/contracts/src/L2/predeploys/L2GasPriceOracle.sol b/contracts/src/L2/predeploys/L2GasPriceOracle.sol new file mode 100644 index 000000000..5d741ae50 --- /dev/null +++ b/contracts/src/L2/predeploys/L2GasPriceOracle.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { OwnableBase } from "../../libraries/common/OwnableBase.sol"; +import { Version } from "../../libraries/common/Version.sol"; + +import "./IL1BlockContainer.sol"; +import "./IL2GasPriceOracle.sol"; + +contract L2GasPriceOracle is Version, OwnableBase, IL2GasPriceOracle { + /************* + * Constants * + *************/ + + /// @dev The precision used in the scalar. + uint256 private constant PRECISION = 1e9; + + /// @dev The maximum possible l1 fee overhead. + /// Computed based on current l1 block gas limit. + uint256 private constant MAX_OVERHEAD = 30000000/16; + + /// @dev The maximum possible l1 fee scale. + /// x1000 should be enough. + uint256 private constant MAX_SCALE = 1000 * PRECISION; + + /// @notice The address of L1BlockContainer contract. + address public immutable blockContainer; + + /************* + * Variables * + *************/ + + /// @inheritdoc IL2GasPriceOracle + uint256 public override overhead; + + /// @inheritdoc IL2GasPriceOracle + uint256 public override scalar; + + /*************** + * Constructor * + ***************/ + + constructor(address _owner, address _blockContainer) { + _transferOwnership(_owner); + + blockContainer = _blockContainer; + } + + /************************* + * Public View Functions * + *************************/ + + /// @inheritdoc IL2GasPriceOracle + function baseFee() external view override returns (uint256) { + return block.basefee; + } + + /// @inheritdoc IL2GasPriceOracle + function gasPrice() external view override returns (uint256) { + return block.basefee; + } + + /// @notice Return the latest known l1 base fee. + function l1BaseFee() public view override returns (uint256) { + return IL1BlockContainer(blockContainer).latestBaseFee(); + } + + /// @inheritdoc IL2GasPriceOracle + function getL1Fee(bytes memory _data) external view override returns (uint256) { + unchecked { + uint256 _l1GasUsed = getL1GasUsed(_data); + uint256 _l1Fee = _l1GasUsed * l1BaseFee(); + return (_l1Fee * scalar) / PRECISION; + } + } + + /// @inheritdoc IL2GasPriceOracle + /// @dev See the comments in `OVM_GasPriceOracle1` for more details + /// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/predeploys/OVM_GasPriceOracle.sol + function getL1GasUsed(bytes memory _data) public view override returns (uint256) { + uint256 _total = 0; + uint256 _length = _data.length; + unchecked { + for (uint256 i = 0; i < _length; i++) { + if (_data[i] == 0) { + _total += 4; + } else { + _total += 16; + } + } + uint256 _unsigned = _total + overhead; + return _unsigned + (68 * 16); + } + } + + /************************ + * Restricted Functions * + ************************/ + + /// @notice Allows the owner to modify the overhead. + /// @param _overhead New overhead + function setOverhead(uint256 _overhead) external onlyOwner { + require(_overhead <= MAX_OVERHEAD, "exceed maximum overhead"); + + overhead = _overhead; + emit OverheadUpdated(_overhead); + } + + /// Allows the owner to modify the scalar. + /// @param _scalar New scalar + function setScalar(uint256 _scalar) external onlyOwner { + require(_scalar <= MAX_SCALE, "exceed maximum scale"); + + scalar = _scalar; + emit ScalarUpdated(_scalar); + } +} diff --git a/contracts/src/L2/predeploys/L2MessageQueue.sol b/contracts/src/L2/predeploys/L2MessageQueue.sol index 2923fee79..382fb9c7d 100644 --- a/contracts/src/L2/predeploys/L2MessageQueue.sol +++ b/contracts/src/L2/predeploys/L2MessageQueue.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import { AppendOnlyMerkleTree } from "../../libraries/common/AppendOnlyMerkleTree.sol"; +import { Version } from "../../libraries/common/Version.sol"; /// @title L2MessageQueue /// @notice The original idea is from Optimism, see [OVM_L2ToL1MessagePasser](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol). @@ -10,7 +11,7 @@ import { AppendOnlyMerkleTree } from "../../libraries/common/AppendOnlyMerkleTre /// of a message on L2. The L1 Cross Domain Messenger performs this proof in its /// _verifyStorageProof function, which verifies the existence of the transaction hash in this /// contract's `sentMessages` mapping. -contract L2MessageQueue is AppendOnlyMerkleTree { +contract L2MessageQueue is Version, AppendOnlyMerkleTree { /// @notice Emitted when a new message is added to the merkle tree. /// @param index The index of the corresponding message. /// @param messageHash The hash of the corresponding message. diff --git a/contracts/src/libraries/common/Version.sol b/contracts/src/libraries/common/Version.sol new file mode 100644 index 000000000..f11b58f45 --- /dev/null +++ b/contracts/src/libraries/common/Version.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +abstract contract Version { + /// @notice the current contract version. + string public constant version = "0.0.1"; +} diff --git a/contracts/src/test/L2GasPriceOracle.t.sol b/contracts/src/test/L2GasPriceOracle.t.sol new file mode 100644 index 000000000..e0f36be96 --- /dev/null +++ b/contracts/src/test/L2GasPriceOracle.t.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol"; +import { WETH } from "solmate/tokens/WETH.sol"; + +import { L1BlockContainer } from "../L2/predeploys/L1BlockContainer.sol"; +import { L2GasPriceOracle } from "../L2/predeploys/L2GasPriceOracle.sol"; + +contract L2GasPriceOracleTest is DSTestPlus { + uint256 private constant PRECISION = 1e9; + uint256 private constant MAX_OVERHEAD = 30000000 / 16; + uint256 private constant MAX_SCALE = 1000 * PRECISION; + + L2GasPriceOracle private oracle; + L1BlockContainer private container; + + function setUp() public { + container = new L1BlockContainer(address(0), address(0)); + oracle = new L2GasPriceOracle(address(this), address(container)); + } + + function testSetOverhead(uint256 _overhead) external { + _overhead = bound(_overhead, 0, MAX_OVERHEAD); + + // call by non-owner, should revert + hevm.startPrank(address(1)); + hevm.expectRevert("caller is not the owner"); + oracle.setOverhead(_overhead); + hevm.stopPrank(); + + // overhead is too large + hevm.expectRevert("exceed maximum overhead"); + oracle.setOverhead(MAX_OVERHEAD + 1); + + // call by owner, should succeed + assertEq(oracle.overhead(), 0); + oracle.setOverhead(_overhead); + assertEq(oracle.overhead(), _overhead); + } + + function testSetScalar(uint256 _scalar) external { + _scalar = bound(_scalar, 0, MAX_SCALE); + + // call by non-owner, should revert + hevm.startPrank(address(1)); + hevm.expectRevert("caller is not the owner"); + oracle.setScalar(_scalar); + hevm.stopPrank(); + + // scale is too large + hevm.expectRevert("exceed maximum scale"); + oracle.setScalar(MAX_SCALE + 1); + + // call by owner, should succeed + assertEq(oracle.scalar(), 0); + oracle.setScalar(_scalar); + assertEq(oracle.scalar(), _scalar); + } + + function testGetL1GasUsed(uint256 _overhead, bytes memory _data) external { + _overhead = bound(_overhead, 0, MAX_OVERHEAD); + + oracle.setOverhead(_overhead); + + uint256 _gasUsed = _overhead + 68 * 16; + for (uint256 i = 0; i < _data.length; i++) { + if (_data[i] == 0) _gasUsed += 4; + else _gasUsed += 16; + } + + assertEq(oracle.getL1GasUsed(_data), _gasUsed); + } + + function testGetL1Fee( + uint256 _baseFee, + uint256 _overhead, + uint256 _scalar, + bytes memory _data + ) external { + _overhead = bound(_overhead, 0, MAX_OVERHEAD); + _scalar = bound(_scalar, 0, MAX_SCALE); + _baseFee = bound(_baseFee, 0, 1e9 * 20000); // max 20k gwei + + oracle.setOverhead(_overhead); + oracle.setScalar(_scalar); + container.initialize(address(0), bytes32(0), 0, 0, uint128(_baseFee), bytes32(0)); + + uint256 _gasUsed = _overhead + 68 * 16; + for (uint256 i = 0; i < _data.length; i++) { + if (_data[i] == 0) _gasUsed += 4; + else _gasUsed += 16; + } + + assertEq(oracle.getL1Fee(_data), (_gasUsed * _baseFee * _scalar) / PRECISION); + } +}