Commit baacda34 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat[contracts]: introduce new L1ChugSplashProxy contract (#1009)

* feat[contracts]: add L1ChugSplashProxy

* improve comments slightly

* start adding tests

* add more tests

* make the system pausable

* added another test

* add some extra comments

* Update packages/contracts/test/contracts/chugsplash/L1ChugSplashProxy.spec.ts
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>

* Update packages/contracts/test/contracts/chugsplash/L1ChugSplashProxy.spec.ts
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>

* chore: add changeset

* address review feedback
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
parent 014dea71
---
'@eth-optimism/contracts': patch
---
Introduce the L1ChugSplashProxy contract
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/**
* @title iL1ChugSplashDeployer
*/
interface iL1ChugSplashDeployer {
function isUpgrading()
external
view
returns (
bool
);
}
import { expect } from '../../setup'
/* Imports: External */
import hre from 'hardhat'
import { Contract, Signer } from 'ethers'
import { smockit } from '@eth-optimism/smock'
/* Imports: Internal */
import { getContractInterface } from '../../../src'
describe('L1ChugSplashProxy', () => {
let signer1: Signer
let signer2: Signer
before(async () => {
;[signer1, signer2] = await hre.ethers.getSigners()
})
let L1ChugSplashProxy: Contract
beforeEach(async () => {
const Factory__L1ChugSplashProxy = await hre.ethers.getContractFactory(
'L1ChugSplashProxy'
)
L1ChugSplashProxy = await Factory__L1ChugSplashProxy.deploy(
await signer1.getAddress()
)
})
describe('getOwner', () => {
it('should return the owner if called by the owner', async () => {
expect(
await L1ChugSplashProxy.connect(signer1).callStatic.getOwner()
).to.equal(await signer1.getAddress())
})
it('should return the owner if called by the zero address in an eth_call', async () => {
expect(
await L1ChugSplashProxy.connect(signer1.provider).callStatic.getOwner({
from: hre.ethers.constants.AddressZero,
})
).to.equal(await signer1.getAddress())
})
it('should otherwise pass through to the proxied contract', async () => {
await expect(
L1ChugSplashProxy.connect(signer2).callStatic.getOwner()
).to.be.revertedWith('L1ChugSplashProxy: implementation is not set yet')
})
})
describe('setOwner', () => {
it('should succeed if called by the owner', async () => {
await expect(
L1ChugSplashProxy.connect(signer1).setOwner(await signer2.getAddress())
).to.not.be.reverted
expect(
await L1ChugSplashProxy.connect(signer2).callStatic.getOwner()
).to.equal(await signer2.getAddress())
})
it('should otherwise pass through to the proxied contract', async () => {
await expect(
L1ChugSplashProxy.connect(signer2).setOwner(await signer1.getAddress())
).to.be.revertedWith('L1ChugSplashProxy: implementation is not set yet')
})
})
describe('getImplementation', () => {
it('should succeed if called by the owner', async () => {
expect(
await L1ChugSplashProxy.connect(signer1).callStatic.getImplementation()
).to.equal(hre.ethers.constants.AddressZero)
})
it('should succeed if called by the zero address in an eth_call', async () => {
expect(
await L1ChugSplashProxy.connect(
hre.ethers.provider
).callStatic.getImplementation({
from: hre.ethers.constants.AddressZero,
})
).to.equal(hre.ethers.constants.AddressZero)
})
it('should otherwise pass through to the proxied contract', async () => {
await expect(
L1ChugSplashProxy.connect(signer2).getImplementation()
).to.be.revertedWith('L1ChugSplashProxy: implementation is not set yet')
})
})
describe('setStorage', () => {
it('should succeed if called by the owner', async () => {
const storageKey = hre.ethers.utils.keccak256('0x1234')
const storageValue = hre.ethers.utils.keccak256('0x5678')
await expect(
L1ChugSplashProxy.connect(signer1).setStorage(storageKey, storageValue)
).to.not.be.reverted
expect(
await hre.ethers.provider.getStorageAt(
L1ChugSplashProxy.address,
storageKey
)
).to.equal(storageValue)
})
it('should otherwise pass through to the proxied contract', async () => {
const storageKey = hre.ethers.utils.keccak256('0x1234')
const storageValue = hre.ethers.utils.keccak256('0x5678')
await expect(
L1ChugSplashProxy.connect(signer2).setStorage(storageKey, storageValue)
).to.be.revertedWith('L1ChugSplashProxy: implementation is not set yet')
})
})
describe('setCode', () => {
it('should succeed if called by the owner', async () => {
const code = '0x1234'
await expect(L1ChugSplashProxy.connect(signer1).setCode(code)).to.not.be
.reverted
const implementation = await L1ChugSplashProxy.connect(
signer1
).callStatic.getImplementation()
expect(await hre.ethers.provider.getCode(implementation)).to.equal(code)
})
it('should not change the implementation address if the code does not change', async () => {
const code = '0x1234'
await L1ChugSplashProxy.connect(signer1).setCode(code)
const implementation = await L1ChugSplashProxy.connect(
signer1
).callStatic.getImplementation()
await L1ChugSplashProxy.connect(signer1).setCode(code)
expect(
await L1ChugSplashProxy.connect(signer1).callStatic.getImplementation()
).to.equal(implementation)
})
})
describe('fallback', () => {
it('should revert if implementation is not set', async () => {
await expect(
signer1.sendTransaction({
to: L1ChugSplashProxy.address,
data: '0x',
})
).to.be.revertedWith('L1ChugSplashProxy: implementation is not set yet')
})
it('should execute the proxied contract when the implementation is set', async () => {
const code = '0x00' // STOP
await L1ChugSplashProxy.connect(signer1).setCode(code)
await expect(
signer1.sendTransaction({
to: L1ChugSplashProxy.address,
data: '0x',
})
).to.not.be.reverted
})
it('should throw an error if the owner has signalled an upgrade', async () => {
const owner = await smockit(getContractInterface('iL1ChugSplashDeployer'))
const factory = await hre.ethers.getContractFactory('L1ChugSplashProxy')
const proxy = await factory.deploy(owner.address)
owner.smocked.isUpgrading.will.return.with(true)
await expect(
owner.wallet.sendTransaction({
to: proxy.address,
data: '0x',
})
).to.be.revertedWith(
'L1ChugSplashProxy: system is currently being upgraded'
)
})
})
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment