diff --git a/.changeset/seven-carpets-tell.md b/.changeset/seven-carpets-tell.md new file mode 100644 index 000000000000..9be51c0a6974 --- /dev/null +++ b/.changeset/seven-carpets-tell.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts': patch +--- + +Introduces the congestion price oracle contract diff --git a/packages/contracts/bin/take-dump.ts b/packages/contracts/bin/take-dump.ts index d77ac6f1c528..95c35680eec4 100644 --- a/packages/contracts/bin/take-dump.ts +++ b/packages/contracts/bin/take-dump.ts @@ -8,6 +8,7 @@ const CHAIN_ID = env.CHAIN_ID || '420' // Defaults to 0xFF....FF = *no upgrades* const L2_CHUG_SPLASH_DEPLOYER_OWNER = env.L2_CHUG_SPLASH_DEPLOYER_OWNER || '0x' + 'FF'.repeat(20) +const GAS_PRICE_ORACLE_OWNER = env.GAS_PRICE_ORACLE_OWNER || '0x' + 'FF'.repeat(20) /* Internal Imports */ import { makeStateDump } from '../src/state-dump/make-dump' @@ -21,7 +22,8 @@ import { RollupDeployConfig } from '../src/contract-deployment' ovmGlobalContext: { ovmCHAINID: parseInt(CHAIN_ID, 10), }, - l2ChugSplashDeployerOwner: L2_CHUG_SPLASH_DEPLOYER_OWNER + l2ChugSplashDeployerOwner: L2_CHUG_SPLASH_DEPLOYER_OWNER, + gasPriceOracleOwner: GAS_PRICE_ORACLE_OWNER } const dump = await makeStateDump(config as RollupDeployConfig) diff --git a/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol new file mode 100644 index 000000000000..6f11866b0857 --- /dev/null +++ b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_GasPriceOracle.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; + +/* External Imports */ +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title OVM_GasPriceOracle + * @dev This contract exposes the current execution price, a measure of how congested the network + * currently is. This measure is used by the Sequencer to determine what fee to charge for + * transactions. When the system is more congested, the execution price will increase and fees + * will also increase as a result. + * + * Compiler used: optimistic-solc + * Runtime target: OVM + */ +contract OVM_GasPriceOracle is Ownable { + + /************* + * Variables * + *************/ + + // Current execution price + uint256 internal executionPrice; + + /*************** + * Constructor * + ***************/ + + /** + * @param _owner Address that will initially own this contract. + */ + constructor( + address _owner + ) + Ownable() + { + transferOwnership(_owner); + } + + + /******************** + * Public Functions * + ********************/ + + /** + * @return Current execution price. + */ + function getExecutionPrice() + public + view + returns ( + uint256 + ) + { + return executionPrice; + } + + /** + * Allows the owner to modify the execution price. + * @param _executionPrice New execution price. + */ + function setExecutionPrice( + uint256 _executionPrice + ) + public + onlyOwner + { + executionPrice = _executionPrice; + } +} diff --git a/packages/contracts/src/contract-deployment/config.ts b/packages/contracts/src/contract-deployment/config.ts index e179624caac3..59e78b5898c7 100644 --- a/packages/contracts/src/contract-deployment/config.ts +++ b/packages/contracts/src/contract-deployment/config.ts @@ -36,6 +36,7 @@ export interface RollupDeployConfig { allowArbitraryContractDeployment: boolean } l2ChugSplashDeployerOwner: string + gasPriceOracleOwner: string addressManager?: string dependencies?: string[] deployOverrides: Overrides @@ -268,5 +269,9 @@ export const makeContractDeployConfig = async ( factory: getContractFactory('L2ChugSplashDeployer'), params: [config.l2ChugSplashDeployerOwner], }, + OVM_GasPriceOracle: { + factory: getContractFactory('OVM_GasPriceOracle'), + params: [config.gasPriceOracleOwner], + }, } } diff --git a/packages/contracts/src/predeploys.ts b/packages/contracts/src/predeploys.ts index db25ad795fdc..5d1ba0d66dfa 100644 --- a/packages/contracts/src/predeploys.ts +++ b/packages/contracts/src/predeploys.ts @@ -20,5 +20,6 @@ export const predeploys = { OVM_ExecutionManagerWrapper: '0x420000000000000000000000000000000000000B', L2ChugSplashDeployer: '0x420000000000000000000000000000000000000D', L2ChugSplashOwner: '0x420000000000000000000000000000000000000E', + OVM_GasPriceOracle: '0x420000000000000000000000000000000000000F', ERC1820Registry: '0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24', } diff --git a/packages/contracts/src/state-dump/make-dump.ts b/packages/contracts/src/state-dump/make-dump.ts index 6a1ea0cbc373..e1442c62e9a5 100644 --- a/packages/contracts/src/state-dump/make-dump.ts +++ b/packages/contracts/src/state-dump/make-dump.ts @@ -139,10 +139,12 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise => { 'OVM_ExecutionManagerWrapper', 'L2ChugSplashDeployer', 'L2ChugSplashOwner', + 'OVM_GasPriceOracle', ], deployOverrides: {}, waitForReceipts: false, l2ChugSplashDeployerOwner: cfg.l2ChugSplashDeployerOwner, + gasPriceOracleOwner: cfg.gasPriceOracleOwner, } config = { ...config, ...cfg } @@ -159,6 +161,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise => { 'OVM_ExecutionManagerWrapper', 'L2ChugSplashDeployer', 'L2ChugSplashOwner', + 'OVM_GasPriceOracle', ] const deploymentResult = await deploy(config) diff --git a/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts b/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts new file mode 100644 index 000000000000..4e86499ef9bb --- /dev/null +++ b/packages/contracts/test/contracts/OVM/precompiles/OVM_GasPriceOracle.spec.ts @@ -0,0 +1,84 @@ +import { expect } from '../../../setup' + +/* External Imports */ +import { ethers } from 'hardhat' +import { ContractFactory, Contract, Signer } from 'ethers' + +describe('OVM_GasPriceOracle', () => { + let signer1: Signer + let signer2: Signer + before(async () => { + ;[signer1, signer2] = await ethers.getSigners() + }) + + let Factory__OVM_GasPriceOracle: ContractFactory + before(async () => { + Factory__OVM_GasPriceOracle = await ethers.getContractFactory( + 'OVM_GasPriceOracle' + ) + }) + + let OVM_GasPriceOracle: Contract + beforeEach(async () => { + OVM_GasPriceOracle = await Factory__OVM_GasPriceOracle.deploy( + await signer1.getAddress() + ) + }) + + describe('owner', () => { + it('should have an owner', async () => { + expect(await OVM_GasPriceOracle.owner()).to.equal( + await signer1.getAddress() + ) + }) + }) + + describe('setExecutionPrice', () => { + it('should revert if called by someone other than the owner', async () => { + await expect(OVM_GasPriceOracle.connect(signer2).setExecutionPrice(1234)) + .to.be.reverted + }) + + it('should succeed if called by the owner', async () => { + await expect(OVM_GasPriceOracle.connect(signer1).setExecutionPrice(1234)) + .to.not.be.reverted + }) + }) + + describe('getExecutionPrice', () => { + it('should return zero at first', async () => { + expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal(0) + }) + + it('should change when setExecutionPrice is called', async () => { + const executionPrice = 1234 + + await OVM_GasPriceOracle.connect(signer1).setExecutionPrice( + executionPrice + ) + + expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal( + executionPrice + ) + }) + + it('is the 1st storage slot', async () => { + const executionPrice = 1234 + const slot = 1 + + // set the price + await OVM_GasPriceOracle.connect(signer1).setExecutionPrice( + executionPrice + ) + + // get the storage slot value + const priceAtSlot = await signer1.provider.getStorageAt( + OVM_GasPriceOracle.address, + slot + ) + expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal( + ethers.BigNumber.from(priceAtSlot) + ) + }) + }) +})