Commit c1e923f9 authored by Matthew Slipper's avatar Matthew Slipper

integration-tests: Changes to make it work with live networks again

parent a9da94ef
---
'@eth-optimism/integration-tests': minor
---
Updates to work with a live network
...@@ -141,6 +141,32 @@ jobs: ...@@ -141,6 +141,32 @@ jobs:
kubectl rollout restart statefulset nightly-dtl --namespace nightly kubectl rollout restart statefulset nightly-dtl --namespace nightly
kubectl rollout restart deployment nightly-gas-oracle --namespace nightly kubectl rollout restart deployment nightly-gas-oracle --namespace nightly
kubectl rollout restart deployment edge-proxyd --namespace nightly kubectl rollout restart deployment edge-proxyd --namespace nightly
run-itests-nightly:
docker:
- image: cimg/base:2021.04
steps:
- setup_remote_docker:
version: 19.03.13
- run:
name: Run integration tests
command: |
docker run \
--env PRIVATE_KEY=$NIGHTLY_ITESTS_PRIVKEY \
--env L1_URL=https://nightly-l1.optimism-stacks.net \
--env L2_URL=https://nightly-l2.optimism-stacks.net \
--env ADDRESS_MANAGER=0x22D4E211ef8704f2ca2d6dfdB32125E2530ACE3e \
--env L2_CHAINID=69 \
--env MOCHA_BAIL=true \
--env MOCHA_TIMEOUT=300000 \
--env L1_GAS_PRICE=onchain \
--env L2_GAS_PRICE=onchain \
--env RUN_DEBUG_TRACE_TESTS=false \
--env RUN_REPLICA_TESTS=false \
--env RUN_STRESS_TESTS=false \
--env OVMCONTEXT_SPEC_NUM_TXS=1 \
--env DTL_ENQUEUE_CONFIRMATIONS=12 \
"$STACKMAN_REPO/integration-tests:nightly" \
yarn test:integration:live
notify: notify:
docker: docker:
- image: cimg/base:2021.04 - image: cimg/base:2021.04
...@@ -152,6 +178,18 @@ jobs: ...@@ -152,6 +178,18 @@ jobs:
workflows: workflows:
nightly-itests:
triggers:
- schedule:
cron: "0 1 * * * "
filters:
branches:
only:
- develop
jobs:
- run-itests-nightly:
context:
- optimism
nightly: nightly:
triggers: triggers:
- schedule: - schedule:
......
...@@ -4,3 +4,14 @@ L1_URL= ...@@ -4,3 +4,14 @@ L1_URL=
L2_URL= L2_URL=
ADDRESS_MANAGER= ADDRESS_MANAGER=
L2_CHAINID= L2_CHAINID=
DTL_ENQUEUE_CONFIRMATIONS=
OVMCONTEXT_SPEC_NUM_TXS=1
# Can be set to true below if the withdrawal window is short enough
RUN_WITHDRAWAL_TESTS=false
RUN_DEBUG_TRACE_TESTS=false
RUN_REPLICA_TESTS=false
RUN_STRESS_TESTS=false
# Can be configured up or down as necessary
MOCHA_TIMEOUT=300000
# Set to true to make Mocha stop after the first failed test.
MOCHA_BAIL=false
\ No newline at end of file
...@@ -4,7 +4,7 @@ import { HardhatUserConfig } from 'hardhat/types' ...@@ -4,7 +4,7 @@ import { HardhatUserConfig } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import '@nomiclabs/hardhat-waffle' import '@nomiclabs/hardhat-waffle'
import 'hardhat-gas-reporter' import 'hardhat-gas-reporter'
import { isLiveNetwork } from './test/shared/utils' import { envConfig } from './test/shared/utils'
const enableGasReport = !!process.env.ENABLE_GAS_REPORT const enableGasReport = !!process.env.ENABLE_GAS_REPORT
...@@ -15,7 +15,8 @@ const config: HardhatUserConfig = { ...@@ -15,7 +15,8 @@ const config: HardhatUserConfig = {
}, },
}, },
mocha: { mocha: {
timeout: isLiveNetwork() ? 300_000 : 75_000, timeout: envConfig.MOCHA_TIMEOUT,
bail: envConfig.MOCHA_BAIL,
}, },
solidity: { solidity: {
compilers: [ compilers: [
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
"@types/chai-as-promised": "^7.1.4", "@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^8.2.2", "@types/mocha": "^8.2.2",
"@types/rimraf": "^3.0.0", "@types/rimraf": "^3.0.0",
"@types/shelljs": "^0.8.8",
"@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0", "@typescript-eslint/parser": "^4.26.0",
"@uniswap/v3-core": "1.0.0", "@uniswap/v3-core": "1.0.0",
...@@ -52,7 +51,6 @@ ...@@ -52,7 +51,6 @@
"chai": "^4.3.4", "chai": "^4.3.4",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"commander": "^8.3.0", "commander": "^8.3.0",
"docker-compose": "^0.23.8",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"envalid": "^7.1.0", "envalid": "^7.1.0",
"eslint": "^7.27.0", "eslint": "^7.27.0",
...@@ -71,7 +69,6 @@ ...@@ -71,7 +69,6 @@
"mocha": "^8.4.0", "mocha": "^8.4.0",
"prom-client": "^14.0.1", "prom-client": "^14.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"shelljs": "^0.8.4",
"typescript": "^4.3.5", "typescript": "^4.3.5",
"uniswap-v3-deploy-plugin": "^0.1.0" "uniswap-v3-deploy-plugin": "^0.1.0"
} }
......
...@@ -8,7 +8,13 @@ import { applyL1ToL2Alias, awaitCondition } from '@eth-optimism/core-utils' ...@@ -8,7 +8,13 @@ import { applyL1ToL2Alias, awaitCondition } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { isMainnet } from './shared/utils' import {
DEFAULT_TEST_GAS_L1,
DEFAULT_TEST_GAS_L2,
envConfig,
sleep,
withdrawalTest,
} from './shared/utils'
describe('Basic L1<>L2 Communication', async () => { describe('Basic L1<>L2 Communication', async () => {
let Factory__L1SimpleStorage: ContractFactory let Factory__L1SimpleStorage: ContractFactory
...@@ -37,42 +43,42 @@ describe('Basic L1<>L2 Communication', async () => { ...@@ -37,42 +43,42 @@ describe('Basic L1<>L2 Communication', async () => {
beforeEach(async () => { beforeEach(async () => {
L1SimpleStorage = await Factory__L1SimpleStorage.deploy() L1SimpleStorage = await Factory__L1SimpleStorage.deploy()
await L1SimpleStorage.deployTransaction.wait() await L1SimpleStorage.deployed()
L2SimpleStorage = await Factory__L2SimpleStorage.deploy() L2SimpleStorage = await Factory__L2SimpleStorage.deploy()
await L2SimpleStorage.deployTransaction.wait() await L2SimpleStorage.deployed()
L2Reverter = await Factory__L2Reverter.deploy() L2Reverter = await Factory__L2Reverter.deploy()
await L2Reverter.deployTransaction.wait() await L2Reverter.deployed()
}) })
describe('L2 => L1', () => { describe('L2 => L1', () => {
it('should be able to perform a withdrawal from L2 -> L1', async function () { withdrawalTest(
if (await isMainnet(env)) { 'should be able to perform a withdrawal from L2 -> L1',
console.log('Skipping withdrawals test on mainnet.') async () => {
this.skip() const value = `0x${'77'.repeat(32)}`
return
} // Send L2 -> L1 message.
const transaction = await env.l2Messenger.sendMessage(
const value = `0x${'77'.repeat(32)}` L1SimpleStorage.address,
L1SimpleStorage.interface.encodeFunctionData('setValue', [value]),
// Send L2 -> L1 message. 5000000,
const transaction = await env.l2Messenger.sendMessage( {
L1SimpleStorage.address, gasLimit: DEFAULT_TEST_GAS_L2,
L1SimpleStorage.interface.encodeFunctionData('setValue', [value]), }
5000000 )
) await transaction.wait()
await transaction.wait() await env.relayXDomainMessages(transaction)
await env.relayXDomainMessages(transaction) await env.waitForXDomainTransaction(transaction, Direction.L2ToL1)
await env.waitForXDomainTransaction(transaction, Direction.L2ToL1)
expect(await L1SimpleStorage.msgSender()).to.equal( expect(await L1SimpleStorage.msgSender()).to.equal(
env.l1Messenger.address env.l1Messenger.address
) )
expect(await L1SimpleStorage.xDomainSender()).to.equal( expect(await L1SimpleStorage.xDomainSender()).to.equal(
env.l2Wallet.address env.l2Wallet.address
) )
expect(await L1SimpleStorage.value()).to.equal(value) expect(await L1SimpleStorage.value()).to.equal(value)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(1) expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(1)
}) }
)
}) })
describe('L1 => L2', () => { describe('L1 => L2', () => {
...@@ -83,7 +89,10 @@ describe('Basic L1<>L2 Communication', async () => { ...@@ -83,7 +89,10 @@ describe('Basic L1<>L2 Communication', async () => {
const transaction = await env.l1Messenger.sendMessage( const transaction = await env.l1Messenger.sendMessage(
L2SimpleStorage.address, L2SimpleStorage.address,
L2SimpleStorage.interface.encodeFunctionData('setValue', [value]), L2SimpleStorage.interface.encodeFunctionData('setValue', [value]),
5000000 5000000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
) )
await env.waitForXDomainTransaction(transaction, Direction.L1ToL2) await env.waitForXDomainTransaction(transaction, Direction.L1ToL2)
...@@ -101,19 +110,41 @@ describe('Basic L1<>L2 Communication', async () => { ...@@ -101,19 +110,41 @@ describe('Basic L1<>L2 Communication', async () => {
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(1) expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(1)
}) })
it('should deposit from L1 -> L2 directly via enqueue', async () => { it('should deposit from L1 -> L2 directly via enqueue', async function () {
this.timeout(
envConfig.MOCHA_TIMEOUT * 2 +
envConfig.DTL_ENQUEUE_CONFIRMATIONS * 15000
)
const value = `0x${'42'.repeat(32)}` const value = `0x${'42'.repeat(32)}`
// Send L1 -> L2 message. // Send L1 -> L2 message.
await env.ctc const tx = await env.ctc
.connect(env.l1Wallet) .connect(env.l1Wallet)
.enqueue( .enqueue(
L2SimpleStorage.address, L2SimpleStorage.address,
5000000, 5000000,
L2SimpleStorage.interface.encodeFunctionData('setValueNotXDomain', [ L2SimpleStorage.interface.encodeFunctionData('setValueNotXDomain', [
value, value,
]) ]),
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
)
const receipt = await tx.wait()
const waitUntilBlock =
receipt.blockNumber + envConfig.DTL_ENQUEUE_CONFIRMATIONS
let currBlock = await env.l1Provider.getBlockNumber()
while (currBlock <= waitUntilBlock) {
const progress =
envConfig.DTL_ENQUEUE_CONFIRMATIONS - (waitUntilBlock - currBlock)
console.log(
`Waiting for ${progress}/${envConfig.DTL_ENQUEUE_CONFIRMATIONS} confirmations.`
) )
await sleep(5000)
currBlock = await env.l1Provider.getBlockNumber()
}
console.log('Enqueue should be confirmed.')
await awaitCondition( await awaitCondition(
async () => { async () => {
...@@ -138,8 +169,12 @@ describe('Basic L1<>L2 Communication', async () => { ...@@ -138,8 +169,12 @@ describe('Basic L1<>L2 Communication', async () => {
const transaction = await env.l1Messenger.sendMessage( const transaction = await env.l1Messenger.sendMessage(
L2SimpleStorage.address, L2SimpleStorage.address,
L2SimpleStorage.interface.encodeFunctionData('setValue', [value]), L2SimpleStorage.interface.encodeFunctionData('setValue', [value]),
5000000 5000000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
) )
await transaction.wait()
const { remoteReceipt } = await env.waitForXDomainTransaction( const { remoteReceipt } = await env.waitForXDomainTransaction(
transaction, transaction,
...@@ -155,7 +190,10 @@ describe('Basic L1<>L2 Communication', async () => { ...@@ -155,7 +190,10 @@ describe('Basic L1<>L2 Communication', async () => {
const transaction = await env.l1Messenger.sendMessage( const transaction = await env.l1Messenger.sendMessage(
L2Reverter.address, L2Reverter.address,
L2Reverter.interface.encodeFunctionData('doRevert', []), L2Reverter.interface.encodeFunctionData('doRevert', []),
5000000 5000000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
) )
const { remoteReceipt } = await env.waitForXDomainTransaction( const { remoteReceipt } = await env.waitForXDomainTransaction(
......
...@@ -5,7 +5,7 @@ import { ethers } from 'hardhat' ...@@ -5,7 +5,7 @@ import { ethers } from 'hardhat'
import * as L2Artifact from '@eth-optimism/contracts/artifacts/contracts/standards/L2StandardERC20.sol/L2StandardERC20.json' import * as L2Artifact from '@eth-optimism/contracts/artifacts/contracts/standards/L2StandardERC20.sol/L2StandardERC20.json'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { isLiveNetwork, isMainnet } from './shared/utils' import { withdrawalTest } from './shared/utils'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
describe('Bridged tokens', () => { describe('Bridged tokens', () => {
...@@ -25,14 +25,16 @@ describe('Bridged tokens', () => { ...@@ -25,14 +25,16 @@ describe('Bridged tokens', () => {
const other = Wallet.createRandom() const other = Wallet.createRandom()
otherWalletL1 = other.connect(env.l1Wallet.provider) otherWalletL1 = other.connect(env.l1Wallet.provider)
otherWalletL2 = other.connect(env.l2Wallet.provider) otherWalletL2 = other.connect(env.l2Wallet.provider)
await env.l1Wallet.sendTransaction({ let tx = await env.l1Wallet.sendTransaction({
to: otherWalletL1.address, to: otherWalletL1.address,
value: utils.parseEther('0.01'), value: utils.parseEther('0.01'),
}) })
await env.l2Wallet.sendTransaction({ await tx.wait()
tx = await env.l2Wallet.sendTransaction({
to: otherWalletL2.address, to: otherWalletL2.address,
value: utils.parseEther('0.01'), value: utils.parseEther('0.01'),
}) })
await tx.wait()
L1Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l1Wallet) L1Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l1Wallet)
L2Factory__ERC20 = new ethers.ContractFactory( L2Factory__ERC20 = new ethers.ContractFactory(
...@@ -77,7 +79,7 @@ describe('Bridged tokens', () => { ...@@ -77,7 +79,7 @@ describe('Bridged tokens', () => {
expect(await L2__ERC20.balanceOf(env.l2Wallet.address)).to.deep.equal( expect(await L2__ERC20.balanceOf(env.l2Wallet.address)).to.deep.equal(
BigNumber.from(1000) BigNumber.from(1000)
) )
}).timeout(isLiveNetwork() ? 300_000 : 120_000) })
it('should transfer tokens on L2', async () => { it('should transfer tokens on L2', async () => {
const tx = await L2__ERC20.transfer(otherWalletL1.address, 500) const tx = await L2__ERC20.transfer(otherWalletL1.address, 500)
...@@ -90,46 +92,40 @@ describe('Bridged tokens', () => { ...@@ -90,46 +92,40 @@ describe('Bridged tokens', () => {
) )
}) })
it('should withdraw tokens from L2 to the depositor', async function () { withdrawalTest(
if (await isMainnet(env)) { 'should withdraw tokens from L2 to the depositor',
console.log('Skipping withdrawals test on mainnet.') async () => {
this.skip() const tx = await env.l2Bridge.withdraw(
return L2__ERC20.address,
500,
2000000,
'0x'
)
await env.relayXDomainMessages(tx)
await env.waitForXDomainTransaction(tx, Direction.L2ToL1)
expect(await L1__ERC20.balanceOf(env.l1Wallet.address)).to.deep.equal(
BigNumber.from(999500)
)
expect(await L2__ERC20.balanceOf(env.l2Wallet.address)).to.deep.equal(
BigNumber.from(0)
)
} }
)
const tx = await env.l2Bridge.withdraw(
L2__ERC20.address, withdrawalTest(
500, 'should withdraw tokens from L2 to the transfer recipient',
2000000, async () => {
'0x' const tx = await env.l2Bridge
) .connect(otherWalletL2)
await env.relayXDomainMessages(tx) .withdraw(L2__ERC20.address, 500, 2000000, '0x')
await env.waitForXDomainTransaction(tx, Direction.L2ToL1) await env.relayXDomainMessages(tx)
expect(await L1__ERC20.balanceOf(env.l1Wallet.address)).to.deep.equal( await env.waitForXDomainTransaction(tx, Direction.L2ToL1)
BigNumber.from(999500) expect(await L1__ERC20.balanceOf(otherWalletL1.address)).to.deep.equal(
) BigNumber.from(500)
expect(await L2__ERC20.balanceOf(env.l2Wallet.address)).to.deep.equal( )
BigNumber.from(0) expect(await L2__ERC20.balanceOf(otherWalletL2.address)).to.deep.equal(
) BigNumber.from(0)
}).timeout(isLiveNetwork() ? 300_000 : 120_000) )
it('should withdraw tokens from L2 to the transfer recipient', async function () {
if (await isMainnet(env)) {
console.log('Skipping withdrawals test on mainnet.')
this.skip()
return
} }
)
const tx = await env.l2Bridge
.connect(otherWalletL2)
.withdraw(L2__ERC20.address, 500, 2000000, '0x')
await env.relayXDomainMessages(tx)
await env.waitForXDomainTransaction(tx, Direction.L2ToL1)
expect(await L1__ERC20.balanceOf(otherWalletL1.address)).to.deep.equal(
BigNumber.from(500)
)
expect(await L2__ERC20.balanceOf(otherWalletL2.address)).to.deep.equal(
BigNumber.from(0)
)
}).timeout(isLiveNetwork() ? 300_000 : 120_000)
}) })
...@@ -6,14 +6,11 @@ import { serialize } from '@ethersproject/transactions' ...@@ -6,14 +6,11 @@ import { serialize } from '@ethersproject/transactions'
import { predeploys, getContractFactory } from '@eth-optimism/contracts' import { predeploys, getContractFactory } from '@eth-optimism/contracts'
/* Imports: Internal */ /* Imports: Internal */
import { isLiveNetwork } from './shared/utils' import { hardhatTest } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
const setPrices = async (env: OptimismEnv, value: number | BigNumber) => { const setPrices = async (env: OptimismEnv, value: number | BigNumber) => {
if (isLiveNetwork()) {
return
}
const gasPrice = await env.gasPriceOracle.setGasPrice(value) const gasPrice = await env.gasPriceOracle.setGasPrice(value)
await gasPrice.wait() await gasPrice.wait()
const baseFee = await env.gasPriceOracle.setL1BaseFee(value) const baseFee = await env.gasPriceOracle.setL1BaseFee(value)
...@@ -28,24 +25,25 @@ describe('Fee Payment Integration Tests', async () => { ...@@ -28,24 +25,25 @@ describe('Fee Payment Integration Tests', async () => {
env = await OptimismEnv.new() env = await OptimismEnv.new()
}) })
if (!isLiveNetwork()) { hardhatTest(
it(`should return eth_gasPrice equal to OVM_GasPriceOracle.gasPrice`, async () => { `should return eth_gasPrice equal to OVM_GasPriceOracle.gasPrice`,
async () => {
const assertGasPrice = async () => { const assertGasPrice = async () => {
const gasPrice = await env.l2Wallet.getGasPrice() const gasPrice = await env.l2Wallet.getGasPrice()
const oracleGasPrice = await env.gasPriceOracle.gasPrice() const oracleGasPrice = await env.gasPriceOracle.gasPrice()
expect(gasPrice).to.deep.equal(oracleGasPrice) expect(gasPrice).to.deep.equal(oracleGasPrice)
} }
assertGasPrice() await assertGasPrice()
// update the gas price // update the gas price
const tx = await env.gasPriceOracle.setGasPrice(1000) const tx = await env.gasPriceOracle.setGasPrice(1000)
await tx.wait() await tx.wait()
assertGasPrice() await assertGasPrice()
}) }
} )
it('Paying a nonzero but acceptable gasPrice fee', async () => { hardhatTest('Paying a nonzero but acceptable gasPrice fee', async () => {
await setPrices(env, 1000) await setPrices(env, 1000)
const amount = utils.parseEther('0.0000001') const amount = utils.parseEther('0.0000001')
...@@ -97,7 +95,7 @@ describe('Fee Payment Integration Tests', async () => { ...@@ -97,7 +95,7 @@ describe('Fee Payment Integration Tests', async () => {
await setPrices(env, 1) await setPrices(env, 1)
}) })
it('should compute correct fee', async () => { hardhatTest('should compute correct fee', async () => {
await setPrices(env, 1000) await setPrices(env, 1000)
const preBalance = await env.l2Wallet.getBalance() const preBalance = await env.l2Wallet.getBalance()
...@@ -149,39 +147,38 @@ describe('Fee Payment Integration Tests', async () => { ...@@ -149,39 +147,38 @@ describe('Fee Payment Integration Tests', async () => {
await expect(env.sequencerFeeVault.withdraw()).to.be.rejected await expect(env.sequencerFeeVault.withdraw()).to.be.rejected
}) })
it('should be able to withdraw fees back to L1 once the minimum is met', async function () { hardhatTest(
if (isLiveNetwork()) { 'should be able to withdraw fees back to L1 once the minimum is met',
this.skip() async () => {
return const l1FeeWallet = await env.sequencerFeeVault.l1FeeWallet()
} const balanceBefore = await env.l1Wallet.provider.getBalance(l1FeeWallet)
const withdrawalAmount =
const l1FeeWallet = await env.sequencerFeeVault.l1FeeWallet() await env.sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
const balanceBefore = await env.l1Wallet.provider.getBalance(l1FeeWallet)
const withdrawalAmount = await env.sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT() // Transfer the minimum required to withdraw.
const tx = await env.l2Wallet.sendTransaction({
// Transfer the minimum required to withdraw. to: env.sequencerFeeVault.address,
const tx = await env.l2Wallet.sendTransaction({ value: withdrawalAmount,
to: env.sequencerFeeVault.address, gasLimit: 500000,
value: withdrawalAmount, })
gasLimit: 500000, await tx.wait()
})
await tx.wait()
const vaultBalance = await env.ovmEth.balanceOf( const vaultBalance = await env.ovmEth.balanceOf(
env.sequencerFeeVault.address env.sequencerFeeVault.address
) )
const withdrawTx = await env.sequencerFeeVault.withdraw() const withdrawTx = await env.sequencerFeeVault.withdraw()
// Wait for the withdrawal to be relayed to L1. // Wait for the withdrawal to be relayed to L1.
await withdrawTx.wait() await withdrawTx.wait()
await env.relayXDomainMessages(withdrawTx) await env.relayXDomainMessages(withdrawTx)
await env.waitForXDomainTransaction(withdrawTx, Direction.L2ToL1) await env.waitForXDomainTransaction(withdrawTx, Direction.L2ToL1)
// Balance difference should be equal to old L2 balance. // Balance difference should be equal to old L2 balance.
const balanceAfter = await env.l1Wallet.provider.getBalance(l1FeeWallet) const balanceAfter = await env.l1Wallet.provider.getBalance(l1FeeWallet)
expect(balanceAfter.sub(balanceBefore)).to.deep.equal( expect(balanceAfter.sub(balanceBefore)).to.deep.equal(
BigNumber.from(vaultBalance) BigNumber.from(vaultBalance)
) )
}) }
)
}) })
...@@ -45,7 +45,7 @@ describe('Native ETH value integration tests', () => { ...@@ -45,7 +45,7 @@ describe('Native ETH value integration tests', () => {
const there = await wallet.sendTransaction({ const there = await wallet.sendTransaction({
to: other.address, to: other.address,
value, value,
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
}) })
const thereReceipt = await there.wait() const thereReceipt = await there.wait()
const thereGas = thereReceipt.gasUsed.mul(there.gasPrice) const thereGas = thereReceipt.gasUsed.mul(there.gasPrice)
...@@ -63,7 +63,7 @@ describe('Native ETH value integration tests', () => { ...@@ -63,7 +63,7 @@ describe('Native ETH value integration tests', () => {
const backAgain = await other.sendTransaction({ const backAgain = await other.sendTransaction({
to: wallet.address, to: wallet.address,
value: backVal, value: backVal,
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
}) })
const backReceipt = await backAgain.wait() const backReceipt = await backAgain.wait()
const backGas = backReceipt.gasUsed.mul(backAgain.gasPrice) const backGas = backReceipt.gasUsed.mul(backAgain.gasPrice)
...@@ -169,7 +169,7 @@ describe('Native ETH value integration tests', () => { ...@@ -169,7 +169,7 @@ describe('Native ETH value integration tests', () => {
it('should allow ETH to be sent', async () => { it('should allow ETH to be sent', async () => {
const sendAmount = 15 const sendAmount = 15
const tx = await ValueCalls0.simpleSend(ValueCalls1.address, sendAmount, { const tx = await ValueCalls0.simpleSend(ValueCalls1.address, sendAmount, {
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
}) })
await tx.wait() await tx.wait()
......
...@@ -9,11 +9,15 @@ import { expectApprox } from '@eth-optimism/core-utils' ...@@ -9,11 +9,15 @@ import { expectApprox } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { isMainnet, PROXY_SEQUENCER_ENTRYPOINT_ADDRESS } from './shared/utils' import {
DEFAULT_TEST_GAS_L1,
DEFAULT_TEST_GAS_L2,
envConfig,
PROXY_SEQUENCER_ENTRYPOINT_ADDRESS,
withdrawalTest,
} from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
const DEFAULT_TEST_GAS_L1 = 330_000
const DEFAULT_TEST_GAS_L2 = 1_300_000
// TX size enforced by CTC: // TX size enforced by CTC:
const MAX_ROLLUP_TX_SIZE = 50_000 const MAX_ROLLUP_TX_SIZE = 50_000
...@@ -183,13 +187,7 @@ describe('Native ETH Integration Tests', async () => { ...@@ -183,13 +187,7 @@ describe('Native ETH Integration Tests', async () => {
).to.be.reverted ).to.be.reverted
}) })
it('withdraw', async function () { withdrawalTest('withdraw', async () => {
if (await isMainnet(env)) {
console.log('Skipping withdrawals test on mainnet.')
this.skip()
return
}
const withdrawAmount = BigNumber.from(3) const withdrawAmount = BigNumber.from(3)
const preBalances = await getBalances(env) const preBalances = await getBalances(env)
expect( expect(
...@@ -231,13 +229,7 @@ describe('Native ETH Integration Tests', async () => { ...@@ -231,13 +229,7 @@ describe('Native ETH Integration Tests', async () => {
) )
}) })
it('withdrawTo', async function () { withdrawalTest('withdrawTo', async () => {
if (await isMainnet(env)) {
console.log('Skipping withdrawals test on mainnet.')
this.skip()
return
}
const withdrawAmount = BigNumber.from(3) const withdrawAmount = BigNumber.from(3)
const preBalances = await getBalances(env) const preBalances = await getBalances(env)
...@@ -295,73 +287,71 @@ describe('Native ETH Integration Tests', async () => { ...@@ -295,73 +287,71 @@ describe('Native ETH Integration Tests', async () => {
) )
}) })
it('deposit, transfer, withdraw', async function () { withdrawalTest(
if (await isMainnet(env)) { 'deposit, transfer, withdraw',
console.log('Skipping withdrawals test on mainnet.') async () => {
this.skip() // 1. deposit
return const amount = utils.parseEther('1')
} await env.waitForXDomainTransaction(
env.l1Bridge.depositETH(DEFAULT_TEST_GAS_L2, '0xFFFF', {
// 1. deposit value: amount,
const amount = utils.parseEther('1') gasLimit: DEFAULT_TEST_GAS_L1,
await env.waitForXDomainTransaction( }),
env.l1Bridge.depositETH(DEFAULT_TEST_GAS_L2, '0xFFFF', { Direction.L1ToL2
value: amount,
gasLimit: DEFAULT_TEST_GAS_L1,
}),
Direction.L1ToL2
)
// 2. transfer to another address
const other = Wallet.createRandom().connect(env.l2Wallet.provider)
const tx = await env.l2Wallet.sendTransaction({
to: other.address,
value: amount,
})
await tx.wait()
const l1BalanceBefore = await other
.connect(env.l1Wallet.provider)
.getBalance()
// 3. do withdrawal
const withdrawnAmount = utils.parseEther('0.95')
const transaction = await env.l2Bridge
.connect(other)
.withdraw(
predeploys.OVM_ETH,
withdrawnAmount,
DEFAULT_TEST_GAS_L1,
'0xFFFF'
) )
await transaction.wait()
await env.relayXDomainMessages(transaction)
const receipts = await env.waitForXDomainTransaction(
transaction,
Direction.L2ToL1
)
// Compute the L1 portion of the fee // 2. transfer to another address
const l1Fee = await env.gasPriceOracle.getL1Fee( const other = Wallet.createRandom().connect(env.l2Wallet.provider)
serialize({ const tx = await env.l2Wallet.sendTransaction({
nonce: transaction.nonce, to: other.address,
value: transaction.value, value: amount,
gasPrice: transaction.gasPrice,
gasLimit: transaction.gasLimit,
to: transaction.to,
data: transaction.data,
}) })
) await tx.wait()
const l1BalanceBefore = await other
.connect(env.l1Wallet.provider)
.getBalance()
// 3. do withdrawal
const withdrawnAmount = utils.parseEther('0.95')
const transaction = await env.l2Bridge
.connect(other)
.withdraw(
predeploys.OVM_ETH,
withdrawnAmount,
DEFAULT_TEST_GAS_L1,
'0xFFFF'
)
await transaction.wait()
await env.relayXDomainMessages(transaction)
const receipts = await env.waitForXDomainTransaction(
transaction,
Direction.L2ToL1
)
// check that correct amount was withdrawn and that fee was charged // Compute the L1 portion of the fee
const l2Fee = receipts.tx.gasPrice.mul(receipts.receipt.gasUsed) const l1Fee = await env.gasPriceOracle.getL1Fee(
serialize({
nonce: transaction.nonce,
value: transaction.value,
gasPrice: transaction.gasPrice,
gasLimit: transaction.gasLimit,
to: transaction.to,
data: transaction.data,
})
)
const fee = l1Fee.add(l2Fee) // check that correct amount was withdrawn and that fee was charged
const l1BalanceAfter = await other const l2Fee = receipts.tx.gasPrice.mul(receipts.receipt.gasUsed)
.connect(env.l1Wallet.provider)
.getBalance() const fee = l1Fee.add(l2Fee)
const l2BalanceAfter = await other.getBalance() const l1BalanceAfter = await other
expect(l1BalanceAfter).to.deep.eq(l1BalanceBefore.add(withdrawnAmount)) .connect(env.l1Wallet.provider)
expect(l2BalanceAfter).to.deep.eq(amount.sub(withdrawnAmount).sub(fee)) .getBalance()
}) const l2BalanceAfter = await other.getBalance()
expect(l1BalanceAfter).to.deep.eq(l1BalanceBefore.add(withdrawnAmount))
expect(l2BalanceAfter).to.deep.eq(amount.sub(withdrawnAmount).sub(fee))
},
envConfig.MOCHA_TIMEOUT * 3
)
}) })
...@@ -7,7 +7,12 @@ import { predeploys } from '@eth-optimism/contracts' ...@@ -7,7 +7,12 @@ import { predeploys } from '@eth-optimism/contracts'
import { Contract, BigNumber } from 'ethers' import { Contract, BigNumber } from 'ethers'
/* Imports: Internal */ /* Imports: Internal */
import { l2Provider, l1Provider, IS_LIVE_NETWORK } from './shared/utils' import {
l2Provider,
l1Provider,
envConfig,
DEFAULT_TEST_GAS_L1,
} from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
...@@ -41,11 +46,7 @@ describe('OVM Context: Layer 2 EVM Context', () => { ...@@ -41,11 +46,7 @@ describe('OVM Context: Layer 2 EVM Context', () => {
await Multicall.deployTransaction.wait() await Multicall.deployTransaction.wait()
}) })
let numTxs = 5 const numTxs = envConfig.OVMCONTEXT_SPEC_NUM_TXS
if (IS_LIVE_NETWORK) {
// Tests take way too long if we don't reduce the number of txs here.
numTxs = 1
}
it('enqueue: L1 contextual values are correctly set in L2', async () => { it('enqueue: L1 contextual values are correctly set in L2', async () => {
for (let i = 0; i < numTxs; i++) { for (let i = 0; i < numTxs; i++) {
...@@ -54,7 +55,10 @@ describe('OVM Context: Layer 2 EVM Context', () => { ...@@ -54,7 +55,10 @@ describe('OVM Context: Layer 2 EVM Context', () => {
const tx = await env.l1Messenger.sendMessage( const tx = await env.l1Messenger.sendMessage(
OVMContextStorage.address, OVMContextStorage.address,
'0x', '0x',
2_000_000 2_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
) )
// Wait for the transaction to be sent over to L2. // Wait for the transaction to be sent over to L2.
...@@ -89,7 +93,7 @@ describe('OVM Context: Layer 2 EVM Context', () => { ...@@ -89,7 +93,7 @@ describe('OVM Context: Layer 2 EVM Context', () => {
const coinbase = await OVMContextStorage.coinbases(i) const coinbase = await OVMContextStorage.coinbases(i)
expect(coinbase).to.equal(predeploys.OVM_SequencerFeeVault) expect(coinbase).to.equal(predeploys.OVM_SequencerFeeVault)
} }
}).timeout(150000) // this specific test takes a while because it involves L1 to L2 txs })
it('should set correct OVM Context for `eth_call`', async () => { it('should set correct OVM Context for `eth_call`', async () => {
for (let i = 0; i < numTxs; i++) { for (let i = 0; i < numTxs; i++) {
......
...@@ -7,7 +7,7 @@ import { injectL2Context, applyL1ToL2Alias } from '@eth-optimism/core-utils' ...@@ -7,7 +7,7 @@ import { injectL2Context, applyL1ToL2Alias } from '@eth-optimism/core-utils'
/* Imports: External */ /* Imports: External */
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { isLiveNetwork } from './shared/utils' import { DEFAULT_TEST_GAS_L1, envConfig } from './shared/utils'
describe('Queue Ingestion', () => { describe('Queue Ingestion', () => {
let env: OptimismEnv let env: OptimismEnv
...@@ -21,7 +21,7 @@ describe('Queue Ingestion', () => { ...@@ -21,7 +21,7 @@ describe('Queue Ingestion', () => {
// that are in the queue and submit them. L2 will pick up the // that are in the queue and submit them. L2 will pick up the
// sequencer batch appended event and play the transactions. // sequencer batch appended event and play the transactions.
it('should order transactions correctly', async () => { it('should order transactions correctly', async () => {
const numTxs = 5 const numTxs = envConfig.OVMCONTEXT_SPEC_NUM_TXS
// Enqueue some transactions by building the calldata and then sending // Enqueue some transactions by building the calldata and then sending
// the transaction to Layer 1 // the transaction to Layer 1
...@@ -30,7 +30,10 @@ describe('Queue Ingestion', () => { ...@@ -30,7 +30,10 @@ describe('Queue Ingestion', () => {
const tx = await env.l1Messenger.sendMessage( const tx = await env.l1Messenger.sendMessage(
`0x${`${i}`.repeat(40)}`, `0x${`${i}`.repeat(40)}`,
`0x0${i}`, `0x0${i}`,
1_000_000 1_000_000,
{
gasLimit: DEFAULT_TEST_GAS_L1,
}
) )
await tx.wait() await tx.wait()
txs.push(tx) txs.push(tx)
...@@ -62,5 +65,5 @@ describe('Queue Ingestion', () => { ...@@ -62,5 +65,5 @@ describe('Queue Ingestion', () => {
) )
expect(l2Tx.l1BlockNumber).to.equal(l1TxReceipt.blockNumber) expect(l2Tx.l1BlockNumber).to.equal(l1TxReceipt.blockNumber)
} }
}).timeout(isLiveNetwork() ? 300_000 : 100_000) })
}) })
...@@ -4,26 +4,26 @@ import { ...@@ -4,26 +4,26 @@ import {
defaultTransactionFactory, defaultTransactionFactory,
gasPriceForL2, gasPriceForL2,
sleep, sleep,
isLiveNetwork, envConfig,
} from './shared/utils' } from './shared/utils'
import { TransactionReceipt } from '@ethersproject/abstract-provider' import { TransactionReceipt } from '@ethersproject/abstract-provider'
describe('Replica Tests', () => { describe('Replica Tests', () => {
let env: OptimismEnv let env: OptimismEnv
before(async () => { before(async function () {
if (!envConfig.RUN_REPLICA_TESTS) {
this.skip()
return
}
env = await OptimismEnv.new() env = await OptimismEnv.new()
}) })
describe('Matching blocks', () => { describe('Matching blocks', () => {
if (isLiveNetwork()) {
console.log('Skipping replica tests on live network')
return
}
it('should sync a transaction', async () => { it('should sync a transaction', async () => {
const tx = defaultTransactionFactory() const tx = defaultTransactionFactory()
tx.gasPrice = await gasPriceForL2(env) tx.gasPrice = await gasPriceForL2()
const result = await env.l2Wallet.sendTransaction(tx) const result = await env.l2Wallet.sendTransaction(tx)
let receipt: TransactionReceipt let receipt: TransactionReceipt
...@@ -48,7 +48,7 @@ describe('Replica Tests', () => { ...@@ -48,7 +48,7 @@ describe('Replica Tests', () => {
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
nonce: await env.l2Wallet.getTransactionCount(), nonce: await env.l2Wallet.getTransactionCount(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
chainId: null, // Disables EIP155 transaction signing. chainId: null, // Disables EIP155 transaction signing.
} }
const signed = await env.l2Wallet.signTransaction(tx) const signed = await env.l2Wallet.signTransaction(tx)
...@@ -76,7 +76,7 @@ describe('Replica Tests', () => { ...@@ -76,7 +76,7 @@ describe('Replica Tests', () => {
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
nonce: await env.l2Wallet.getTransactionCount(), nonce: await env.l2Wallet.getTransactionCount(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
} }
const signed = await env.l2Wallet.signTransaction(tx) const signed = await env.l2Wallet.signTransaction(tx)
const result = await env.replicaProvider.sendTransaction(signed) const result = await env.replicaProvider.sendTransaction(signed)
......
...@@ -10,8 +10,10 @@ import { ...@@ -10,8 +10,10 @@ import {
defaultTransactionFactory, defaultTransactionFactory,
fundUser, fundUser,
L2_CHAINID, L2_CHAINID,
isLiveNetwork,
gasPriceForL2, gasPriceForL2,
isHardhat,
hardhatTest,
envConfig,
} from './shared/utils' } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { import {
...@@ -56,7 +58,7 @@ describe('Basic RPC tests', () => { ...@@ -56,7 +58,7 @@ describe('Basic RPC tests', () => {
describe('eth_sendRawTransaction', () => { describe('eth_sendRawTransaction', () => {
it('should correctly process a valid transaction', async () => { it('should correctly process a valid transaction', async () => {
const tx = defaultTransactionFactory() const tx = defaultTransactionFactory()
tx.gasPrice = await gasPriceForL2(env) tx.gasPrice = await gasPriceForL2()
const nonce = await wallet.getTransactionCount() const nonce = await wallet.getTransactionCount()
const result = await wallet.sendTransaction(tx) const result = await wallet.sendTransaction(tx)
...@@ -70,7 +72,7 @@ describe('Basic RPC tests', () => { ...@@ -70,7 +72,7 @@ describe('Basic RPC tests', () => {
it('should not accept a transaction with the wrong chain ID', async () => { it('should not accept a transaction with the wrong chain ID', async () => {
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
chainId: (await wallet.getChainId()) + 1, chainId: (await wallet.getChainId()) + 1,
} }
...@@ -83,7 +85,7 @@ describe('Basic RPC tests', () => { ...@@ -83,7 +85,7 @@ describe('Basic RPC tests', () => {
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
nonce: await wallet.getTransactionCount(), nonce: await wallet.getTransactionCount(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
chainId: null, // Disables EIP155 transaction signing. chainId: null, // Disables EIP155 transaction signing.
} }
const signed = await wallet.signTransaction(tx) const signed = await wallet.signTransaction(tx)
...@@ -97,7 +99,7 @@ describe('Basic RPC tests', () => { ...@@ -97,7 +99,7 @@ describe('Basic RPC tests', () => {
it('should accept a transaction with a value', async () => { it('should accept a transaction with a value', async () => {
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
chainId: await env.l2Wallet.getChainId(), chainId: await env.l2Wallet.getChainId(),
data: '0x', data: '0x',
value: ethers.utils.parseEther('0.1'), value: ethers.utils.parseEther('0.1'),
...@@ -117,7 +119,7 @@ describe('Basic RPC tests', () => { ...@@ -117,7 +119,7 @@ describe('Basic RPC tests', () => {
const balance = await env.l2Wallet.getBalance() const balance = await env.l2Wallet.getBalance()
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
gasPrice: await gasPriceForL2(env), gasPrice: await gasPriceForL2(),
chainId: await env.l2Wallet.getChainId(), chainId: await env.l2Wallet.getChainId(),
data: '0x', data: '0x',
value: balance.add(ethers.utils.parseEther('1')), value: balance.add(ethers.utils.parseEther('1')),
...@@ -137,32 +139,29 @@ describe('Basic RPC tests', () => { ...@@ -137,32 +139,29 @@ describe('Basic RPC tests', () => {
}) })
it('should reject a transaction with too low of a fee', async () => { it('should reject a transaction with too low of a fee', async () => {
if (isLiveNetwork()) { const isHH = await isHardhat()
console.log('Skipping too low of a fee test on live network') let gasPrice
return if (isHH) {
gasPrice = await env.gasPriceOracle.gasPrice()
await env.gasPriceOracle.setGasPrice(1000)
} }
const gasPrice = await env.gasPriceOracle.gasPrice()
await env.gasPriceOracle.setGasPrice(1000)
const tx = { const tx = {
...defaultTransactionFactory(), ...defaultTransactionFactory(),
gasPrice: 1, gasPrice: 1,
} }
await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith( await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith(
`gas price too low: 1 wei, use at least tx.gasPrice = 1000 wei` /gas price too low: 1 wei, use at least tx\.gasPrice = \d+ wei/
) )
// Reset the gas price to its original price
await env.gasPriceOracle.setGasPrice(gasPrice)
})
it('should reject a transaction with too high of a fee', async () => { if (isHH) {
if (isLiveNetwork()) { // Reset the gas price to its original price
console.log('Skpping too high of a fee test on live network') await env.gasPriceOracle.setGasPrice(gasPrice)
return
} }
})
it('should reject a transaction with too high of a fee', async () => {
const gasPrice = await env.gasPriceOracle.gasPrice() const gasPrice = await env.gasPriceOracle.gasPrice()
const largeGasPrice = gasPrice.mul(10) const largeGasPrice = gasPrice.mul(10)
const tx = { const tx = {
...@@ -332,7 +331,7 @@ describe('Basic RPC tests', () => { ...@@ -332,7 +331,7 @@ describe('Basic RPC tests', () => {
it('includes L1 gas price and L1 gas used', async () => { it('includes L1 gas price and L1 gas used', async () => {
const tx = await env.l2Wallet.populateTransaction({ const tx = await env.l2Wallet.populateTransaction({
to: env.l2Wallet.address, to: env.l2Wallet.address,
gasPrice: isLiveNetwork() ? 10000 : 1, gasPrice: await gasPriceForL2(),
}) })
const raw = serialize({ const raw = serialize({
...@@ -367,7 +366,7 @@ describe('Basic RPC tests', () => { ...@@ -367,7 +366,7 @@ describe('Basic RPC tests', () => {
describe('eth_getTransactionByHash', () => { describe('eth_getTransactionByHash', () => {
it('should be able to get all relevant l1/l2 transaction data', async () => { it('should be able to get all relevant l1/l2 transaction data', async () => {
const tx = defaultTransactionFactory() const tx = defaultTransactionFactory()
tx.gasPrice = await gasPriceForL2(env) tx.gasPrice = await gasPriceForL2()
const result = await wallet.sendTransaction(tx) const result = await wallet.sendTransaction(tx)
await result.wait() await result.wait()
...@@ -382,7 +381,7 @@ describe('Basic RPC tests', () => { ...@@ -382,7 +381,7 @@ describe('Basic RPC tests', () => {
it('should return the block and all included transactions', async () => { it('should return the block and all included transactions', async () => {
// Send a transaction and wait for it to be mined. // Send a transaction and wait for it to be mined.
const tx = defaultTransactionFactory() const tx = defaultTransactionFactory()
tx.gasPrice = await gasPriceForL2(env) tx.gasPrice = await gasPriceForL2()
const result = await wallet.sendTransaction(tx) const result = await wallet.sendTransaction(tx)
const receipt = await result.wait() const receipt = await result.wait()
...@@ -408,32 +407,31 @@ describe('Basic RPC tests', () => { ...@@ -408,32 +407,31 @@ describe('Basic RPC tests', () => {
// Needs to be skipped on Prod networks because this test doesn't work when // Needs to be skipped on Prod networks because this test doesn't work when
// other people are sending transactions to the Sequencer at the same time // other people are sending transactions to the Sequencer at the same time
// as this test is running. // as this test is running.
it('should return the same result when new transactions are not applied', async function () { hardhatTest(
if (isLiveNetwork()) { 'should return the same result when new transactions are not applied',
this.skip() async () => {
} // Get latest block once to start.
const prev = await provider.getBlockWithTransactions('latest')
// Get latest block once to start. // set wait to null to allow a deep object comparison
const prev = await provider.getBlockWithTransactions('latest') prev.transactions[0].wait = null
// set wait to null to allow a deep object comparison
prev.transactions[0].wait = null // Over ten seconds, repeatedly check the latest block to make sure nothing has changed.
for (let i = 0; i < 5; i++) {
// Over ten seconds, repeatedly check the latest block to make sure nothing has changed. const latest = await provider.getBlockWithTransactions('latest')
for (let i = 0; i < 5; i++) { latest.transactions[0].wait = null
const latest = await provider.getBlockWithTransactions('latest') // Check each key of the transaction individually
latest.transactions[0].wait = null // for easy debugging if one field changes
// Check each key of the transaction individually for (const [key, value] of Object.entries(latest.transactions[0])) {
// for easy debugging if one field changes expect(value).to.deep.equal(
for (const [key, value] of Object.entries(latest.transactions[0])) { prev.transactions[0][key],
expect(value).to.deep.equal( `mismatch ${key}`
prev.transactions[0][key], )
`mismatch ${key}` }
) expect(latest).to.deep.equal(prev)
await sleep(2000)
} }
expect(latest).to.deep.equal(prev)
await sleep(2000)
} }
}) )
}) })
describe('eth_getBalance', () => { describe('eth_getBalance', () => {
...@@ -489,6 +487,12 @@ describe('Basic RPC tests', () => { ...@@ -489,6 +487,12 @@ describe('Basic RPC tests', () => {
}) })
describe('debug_traceTransaction', () => { describe('debug_traceTransaction', () => {
before(async function () {
if (!envConfig.RUN_DEBUG_TRACE_TESTS) {
this.skip()
}
})
it('should match debug_traceBlock', async () => { it('should match debug_traceBlock', async () => {
const storage = await ethers.getContractFactory( const storage = await ethers.getContractFactory(
'SimpleStorage', 'SimpleStorage',
......
import { DockerComposeNetwork } from './shared/docker-compose'
before(async () => {
if (!process.env.NO_NETWORK) {
await new DockerComposeNetwork().up()
}
})
import * as compose from 'docker-compose'
import * as shell from 'shelljs'
import * as path from 'path'
type ServiceNames =
| 'batch_submitter'
| 'dtl'
| 'l2geth'
| 'relayer'
| 'verifier'
| 'replica'
const OPS_DIRECTORY = path.join(process.cwd(), '../ops')
const DEFAULT_SERVICES: ServiceNames[] = [
'batch_submitter',
'dtl',
'l2geth',
'relayer',
]
export class DockerComposeNetwork {
constructor(private readonly services: ServiceNames[] = DEFAULT_SERVICES) {}
async up(options?: compose.IDockerComposeOptions) {
const out = await compose.upMany(this.services, {
cwd: OPS_DIRECTORY,
...options,
})
const { err, exitCode } = out
if (!err || exitCode) {
console.error(err)
throw new Error(
'Unexpected error when starting docker-compose network, dumping output'
)
}
if (err.includes('Creating')) {
console.info(
'🐳 Tests required starting containers. Waiting for sequencer to ready.'
)
shell.exec(`${OPS_DIRECTORY}/scripts/wait-for-sequencer.sh`, {
cwd: OPS_DIRECTORY,
})
}
return out
}
async logs() {
return compose.logs(this.services, { cwd: OPS_DIRECTORY })
}
async stop(service: ServiceNames) {
return compose.stopOne(service, { cwd: OPS_DIRECTORY })
}
async rm() {
return compose.rm({ cwd: OPS_DIRECTORY })
}
}
...@@ -19,6 +19,8 @@ import { ...@@ -19,6 +19,8 @@ import {
getL1Bridge, getL1Bridge,
getL2Bridge, getL2Bridge,
sleep, sleep,
envConfig,
DEFAULT_TEST_GAS_L1,
} from './utils' } from './utils'
import { import {
initWatcher, initWatcher,
...@@ -83,8 +85,10 @@ export class OptimismEnv { ...@@ -83,8 +85,10 @@ export class OptimismEnv {
// fund the user if needed // fund the user if needed
const balance = await l2Wallet.getBalance() const balance = await l2Wallet.getBalance()
if (balance.lt(utils.parseEther('1'))) { const min = envConfig.L2_WALLET_MIN_BALANCE_ETH.toString()
await fundUser(watcher, l1Bridge, utils.parseEther('1').sub(balance)) const topUp = envConfig.L2_WALLET_TOP_UP_AMOUNT_ETH.toString()
if (balance.lt(utils.parseEther(min))) {
await fundUser(watcher, l1Bridge, utils.parseEther(topUp))
} }
const l1Messenger = getContractFactory('L1CrossDomainMessenger') const l1Messenger = getContractFactory('L1CrossDomainMessenger')
.connect(l1Wallet) .connect(l1Wallet)
...@@ -156,6 +160,7 @@ export class OptimismEnv { ...@@ -156,6 +160,7 @@ export class OptimismEnv {
tx: Promise<TransactionResponse> | TransactionResponse tx: Promise<TransactionResponse> | TransactionResponse
): Promise<void> { ): Promise<void> {
tx = await tx tx = await tx
await tx.wait()
let messagePairs = [] let messagePairs = []
while (true) { while (true) {
...@@ -187,7 +192,10 @@ export class OptimismEnv { ...@@ -187,7 +192,10 @@ export class OptimismEnv {
message.sender, message.sender,
message.message, message.message,
message.messageNonce, message.messageNonce,
proof proof,
{
gasLimit: DEFAULT_TEST_GAS_L1 * 10,
}
) )
await result.wait() await result.wait()
break break
......
...@@ -23,7 +23,7 @@ export const fundRandomWallet = async ( ...@@ -23,7 +23,7 @@ export const fundRandomWallet = async (
const fundTx = await env.l1Wallet.sendTransaction({ const fundTx = await env.l1Wallet.sendTransaction({
gasLimit: 25_000, gasLimit: 25_000,
to: wallet.address, to: wallet.address,
gasPrice: await gasPriceForL1(env), gasPrice: await gasPriceForL1(),
value, value,
}) })
await fundTx.wait() await fundTx.wait()
...@@ -47,7 +47,7 @@ export const executeL1ToL2Transaction = async ( ...@@ -47,7 +47,7 @@ export const executeL1ToL2Transaction = async (
), ),
MESSAGE_GAS, MESSAGE_GAS,
{ {
gasPrice: await gasPriceForL1(env), gasPrice: await gasPriceForL1(),
} }
) )
) )
...@@ -71,7 +71,7 @@ export const executeL2ToL1Transaction = async ( ...@@ -71,7 +71,7 @@ export const executeL2ToL1Transaction = async (
), ),
MESSAGE_GAS, MESSAGE_GAS,
{ {
gasPrice: gasPriceForL2(env), gasPrice: gasPriceForL2(),
} }
) )
) )
...@@ -90,7 +90,7 @@ export const executeL2Transaction = async ( ...@@ -90,7 +90,7 @@ export const executeL2Transaction = async (
tx.contract tx.contract
.connect(signer) .connect(signer)
.functions[tx.functionName](...tx.functionParams, { .functions[tx.functionName](...tx.functionParams, {
gasPrice: gasPriceForL2(env), gasPrice: gasPriceForL2(),
}) })
) )
await result.wait() await result.wait()
......
...@@ -14,32 +14,54 @@ import { ...@@ -14,32 +14,54 @@ import {
predeploys, predeploys,
} from '@eth-optimism/contracts' } from '@eth-optimism/contracts'
import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils' import { injectL2Context, remove0x, Watcher } from '@eth-optimism/core-utils'
import { cleanEnv, str, num, bool } from 'envalid' import { cleanEnv, str, num, bool, makeValidator } from 'envalid'
import dotenv from 'dotenv' import dotenv from 'dotenv'
dotenv.config()
/* Imports: Internal */ /* Imports: Internal */
import { Direction, waitForXDomainTransaction } from './watcher-utils' import { Direction, waitForXDomainTransaction } from './watcher-utils'
import { OptimismEnv } from './env' import { OptimismEnv } from './env'
export const GWEI = BigNumber.from(1e9)
export const isLiveNetwork = () => { export const isLiveNetwork = () => {
return process.env.IS_LIVE_NETWORK === 'true' return process.env.IS_LIVE_NETWORK === 'true'
} }
if (isLiveNetwork()) { export const HARDHAT_CHAIN_ID = 31337
dotenv.config() export const DEFAULT_TEST_GAS_L1 = 330_000
} export const DEFAULT_TEST_GAS_L2 = 1_300_000
export const ON_CHAIN_GAS_PRICE = 'onchain'
const gasPriceValidator = makeValidator((gasPrice) => {
if (gasPrice === 'onchain') {
return gasPrice
}
const env = cleanEnv(process.env, { return num()._parse(gasPrice).toString()
})
const procEnv = cleanEnv(process.env, {
L1_GAS_PRICE: gasPriceValidator({
default: '0',
}),
L1_URL: str({ default: 'http://localhost:9545' }), L1_URL: str({ default: 'http://localhost:9545' }),
L2_URL: str({ default: 'http://localhost:8545' }),
VERIFIER_URL: str({ default: 'http://localhost:8547' }),
REPLICA_URL: str({ default: 'http://localhost:8549' }),
L1_POLLING_INTERVAL: num({ default: 10 }), L1_POLLING_INTERVAL: num({ default: 10 }),
L2_CHAINID: num({ default: 420 }),
L2_GAS_PRICE: gasPriceValidator({
default: 'onchain',
}),
L2_URL: str({ default: 'http://localhost:8545' }),
L2_POLLING_INTERVAL: num({ default: 10 }), L2_POLLING_INTERVAL: num({ default: 10 }),
VERIFIER_POLLING_INTERVAL: num({ default: 10 }), L2_WALLET_MIN_BALANCE_ETH: num({
default: 2,
}),
L2_WALLET_TOP_UP_AMOUNT_ETH: num({
default: 3,
}),
REPLICA_URL: str({ default: 'http://localhost:8549' }),
REPLICA_POLLING_INTERVAL: num({ default: 10 }), REPLICA_POLLING_INTERVAL: num({ default: 10 }),
PRIVATE_KEY: str({ PRIVATE_KEY: str({
default: default:
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
...@@ -51,31 +73,53 @@ const env = cleanEnv(process.env, { ...@@ -51,31 +73,53 @@ const env = cleanEnv(process.env, {
default: default:
'0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba', '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
}), }),
L2_CHAINID: num({ default: 420 }),
IS_LIVE_NETWORK: bool({ default: false }), OVMCONTEXT_SPEC_NUM_TXS: num({
default: 5,
}),
DTL_ENQUEUE_CONFIRMATIONS: num({
default: 0,
}),
RUN_WITHDRAWAL_TESTS: bool({
default: true,
}),
RUN_REPLICA_TESTS: bool({
default: true,
}),
RUN_DEBUG_TRACE_TESTS: bool({
default: true,
}),
RUN_STRESS_TESTS: bool({
default: true,
}),
MOCHA_TIMEOUT: num({
default: 120_000,
}),
MOCHA_BAIL: bool({
default: false,
}),
}) })
export const envConfig = procEnv
// The hardhat instance // The hardhat instance
export const l1Provider = new providers.JsonRpcProvider(env.L1_URL) export const l1Provider = new providers.JsonRpcProvider(procEnv.L1_URL)
l1Provider.pollingInterval = env.L1_POLLING_INTERVAL l1Provider.pollingInterval = procEnv.L1_POLLING_INTERVAL
export const l2Provider = injectL2Context( export const l2Provider = injectL2Context(
new providers.JsonRpcProvider(env.L2_URL) new providers.JsonRpcProvider(procEnv.L2_URL)
) )
l2Provider.pollingInterval = env.L2_POLLING_INTERVAL l2Provider.pollingInterval = procEnv.L2_POLLING_INTERVAL
export const verifierProvider = injectL2Context(
new providers.JsonRpcProvider(env.VERIFIER_URL)
)
verifierProvider.pollingInterval = env.VERIFIER_POLLING_INTERVAL
export const replicaProvider = injectL2Context( export const replicaProvider = injectL2Context(
new providers.JsonRpcProvider(env.REPLICA_URL) new providers.JsonRpcProvider(procEnv.REPLICA_URL)
) )
replicaProvider.pollingInterval = env.REPLICA_POLLING_INTERVAL replicaProvider.pollingInterval = procEnv.REPLICA_POLLING_INTERVAL
// The sequencer private key which is funded on L1 // The sequencer private key which is funded on L1
export const l1Wallet = new Wallet(env.PRIVATE_KEY, l1Provider) export const l1Wallet = new Wallet(procEnv.PRIVATE_KEY, l1Provider)
// A random private key which should always be funded with deposits from L1 -> L2 // A random private key which should always be funded with deposits from L1 -> L2
// if it's using non-0 gas price // if it's using non-0 gas price
...@@ -83,7 +127,7 @@ export const l2Wallet = l1Wallet.connect(l2Provider) ...@@ -83,7 +127,7 @@ export const l2Wallet = l1Wallet.connect(l2Provider)
// The owner of the GasPriceOracle on L2 // The owner of the GasPriceOracle on L2
export const gasPriceOracleWallet = new Wallet( export const gasPriceOracleWallet = new Wallet(
env.GAS_PRICE_ORACLE_PRIVATE_KEY, procEnv.GAS_PRICE_ORACLE_PRIVATE_KEY,
l2Provider l2Provider
) )
...@@ -92,13 +136,12 @@ export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS = ...@@ -92,13 +136,12 @@ export const PROXY_SEQUENCER_ENTRYPOINT_ADDRESS =
'0x4200000000000000000000000000000000000004' '0x4200000000000000000000000000000000000004'
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
export const L2_CHAINID = env.L2_CHAINID export const L2_CHAINID = procEnv.L2_CHAINID
export const IS_LIVE_NETWORK = env.IS_LIVE_NETWORK
export const getAddressManager = (provider: any) => { export const getAddressManager = (provider: any) => {
return getContractFactory('Lib_AddressManager') return getContractFactory('Lib_AddressManager')
.connect(provider) .connect(provider)
.attach(env.ADDRESS_MANAGER) .attach(procEnv.ADDRESS_MANAGER)
} }
// Gets the bridge contract // Gets the bridge contract
...@@ -115,33 +158,17 @@ export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => { ...@@ -115,33 +158,17 @@ export const getL1Bridge = async (wallet: Wallet, AddressManager: Contract) => {
throw new Error('Proxy__OVM_L1StandardBridge not found') throw new Error('Proxy__OVM_L1StandardBridge not found')
} }
const L1StandardBridge = new Contract( return new Contract(ProxyBridgeAddress, l1BridgeInterface, wallet)
ProxyBridgeAddress,
l1BridgeInterface,
wallet
)
return L1StandardBridge
} }
export const getL2Bridge = async (wallet: Wallet) => { export const getL2Bridge = async (wallet: Wallet) => {
const L2BridgeInterface = getContractInterface('L2StandardBridge') const L2BridgeInterface = getContractInterface('L2StandardBridge')
const L2StandardBridge = new Contract( return new Contract(predeploys.L2StandardBridge, L2BridgeInterface, wallet)
predeploys.L2StandardBridge,
L2BridgeInterface,
wallet
)
return L2StandardBridge
} }
export const getOvmEth = (wallet: Wallet) => { export const getOvmEth = (wallet: Wallet) => {
const OVM_ETH = new Contract( return new Contract(OVM_ETH_ADDRESS, getContractInterface('OVM_ETH'), wallet)
OVM_ETH_ADDRESS,
getContractInterface('OVM_ETH'),
wallet
)
return OVM_ETH
} }
export const fundUser = async ( export const fundUser = async (
...@@ -152,12 +179,54 @@ export const fundUser = async ( ...@@ -152,12 +179,54 @@ export const fundUser = async (
) => { ) => {
const value = BigNumber.from(amount) const value = BigNumber.from(amount)
const tx = recipient const tx = recipient
? bridge.depositETHTo(recipient, 1_300_000, '0x', { value }) ? bridge.depositETHTo(recipient, DEFAULT_TEST_GAS_L2, '0x', {
: bridge.depositETH(1_300_000, '0x', { value }) value,
gasLimit: DEFAULT_TEST_GAS_L1,
})
: bridge.depositETH(DEFAULT_TEST_GAS_L2, '0x', {
value,
gasLimit: DEFAULT_TEST_GAS_L1,
})
await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2) await waitForXDomainTransaction(watcher, tx, Direction.L1ToL2)
} }
export const conditionalTest = (
condition: (env?: OptimismEnv) => Promise<boolean>,
name,
fn,
message?: string,
timeout?: number
) => {
it(name, async function () {
const shouldRun = await condition()
if (!shouldRun) {
console.log(message)
this.skip()
return
}
await fn()
}).timeout(timeout || envConfig.MOCHA_TIMEOUT)
}
export const withdrawalTest = (name, fn, timeout?: number) =>
conditionalTest(
() => Promise.resolve(procEnv.RUN_WITHDRAWAL_TESTS),
name,
fn,
`Skipping withdrawal test.`,
timeout
)
export const hardhatTest = (name, fn) =>
conditionalTest(
isHardhat,
name,
fn,
'Skipping test on non-Hardhat environment.'
)
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)) export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
const abiCoder = new utils.AbiCoder() const abiCoder = new utils.AbiCoder()
...@@ -175,56 +244,23 @@ export const defaultTransactionFactory = () => { ...@@ -175,56 +244,23 @@ export const defaultTransactionFactory = () => {
} }
} }
export const waitForL2Geth = async ( export const gasPriceForL2 = async () => {
provider: providers.JsonRpcProvider if (procEnv.L2_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
): Promise<providers.JsonRpcProvider> => { return l2Wallet.getGasPrice()
let ready: boolean = false
while (!ready) {
try {
await provider.getNetwork()
ready = true
} catch (error) {
await sleep(1000)
}
}
return injectL2Context(provider)
}
// eslint-disable-next-line @typescript-eslint/no-shadow
export const gasPriceForL2 = async (env: OptimismEnv) => {
// The integration tests enforce fees on L2
// which run against hardhat on L1. Update if
// geth --dev is adopted for L1
const chainId = await env.l1Wallet.getChainId()
if ((await isMainnet(env)) || chainId === 31337) {
return env.l2Wallet.getGasPrice()
} }
if (isLiveNetwork()) { return utils.parseUnits(procEnv.L2_GAS_PRICE, 'wei')
return Promise.resolve(BigNumber.from(10000))
}
return Promise.resolve(BigNumber.from(0))
} }
// eslint-disable-next-line @typescript-eslint/no-shadow export const gasPriceForL1 = async () => {
export const gasPriceForL1 = async (env: OptimismEnv) => { if (procEnv.L1_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
const chainId = await env.l1Wallet.getChainId() return l1Wallet.getGasPrice()
switch (chainId) {
case 1:
return env.l1Wallet.getGasPrice()
case 3:
case 42:
return utils.parseUnits('10', 'gwei')
case 5:
return utils.parseUnits('2', 'gwei')
default:
return BigNumber.from(0)
} }
return utils.parseUnits(procEnv.L1_GAS_PRICE, 'wei')
} }
// eslint-disable-next-line @typescript-eslint/no-shadow export const isHardhat = async () => {
export const isMainnet = async (env: OptimismEnv) => { const chainId = await l1Wallet.getChainId()
const chainId = await env.l1Wallet.getChainId() return chainId === HARDHAT_CHAIN_ID
return chainId === 1
} }
...@@ -17,12 +17,12 @@ import { ...@@ -17,12 +17,12 @@ import {
} from './shared/stress-test-helpers' } from './shared/stress-test-helpers'
/* Imports: Artifacts */ /* Imports: Artifacts */
import { fundUser, isLiveNetwork, isMainnet } from './shared/utils' import { envConfig, fundUser } from './shared/utils'
// Need a big timeout to allow for all transactions to be processed. // Need a big timeout to allow for all transactions to be processed.
// For some reason I can't figure out how to set the timeout on a per-suite basis // For some reason I can't figure out how to set the timeout on a per-suite basis
// so I'm instead setting it for every test. // so I'm instead setting it for every test.
const STRESS_TEST_TIMEOUT = isLiveNetwork() ? 500_000 : 1_200_000 const STRESS_TEST_TIMEOUT = envConfig.MOCHA_TIMEOUT * 5
describe('stress tests', () => { describe('stress tests', () => {
const numTransactions = 3 const numTransactions = 3
...@@ -32,13 +32,14 @@ describe('stress tests', () => { ...@@ -32,13 +32,14 @@ describe('stress tests', () => {
const wallets: Wallet[] = [] const wallets: Wallet[] = []
before(async function () { before(async function () {
env = await OptimismEnv.new() if (!envConfig.RUN_STRESS_TESTS) {
if (await isMainnet(env)) { console.log('Skipping stress tests.')
console.log('Skipping stress tests on mainnet.')
this.skip() this.skip()
return return
} }
env = await OptimismEnv.new()
for (let i = 0; i < numTransactions; i++) { for (let i = 0; i < numTransactions; i++) {
wallets.push(Wallet.createRandom()) wallets.push(Wallet.createRandom())
} }
......
...@@ -3210,14 +3210,6 @@ ...@@ -3210,14 +3210,6 @@
"@types/mime" "^1" "@types/mime" "^1"
"@types/node" "*" "@types/node" "*"
"@types/shelljs@^0.8.8":
version "0.8.9"
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.9.tgz#45dd8501aa9882976ca3610517dac3831c2fbbf4"
integrity sha512-flVe1dvlrCyQJN/SGrnBxqHG+RzXrVKsmjD8WS/qYHpq5UPjfq7UWFBENP0ZuOl0g6OpAlL6iBoLSvKYUUmyQw==
dependencies:
"@types/glob" "*"
"@types/node" "*"
"@types/sinon-chai@^3.2.3", "@types/sinon-chai@^3.2.5": "@types/sinon-chai@^3.2.3", "@types/sinon-chai@^3.2.5":
version "3.2.5" version "3.2.5"
resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48" resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48"
...@@ -6247,13 +6239,6 @@ directory-tree@^2.2.7: ...@@ -6247,13 +6239,6 @@ directory-tree@^2.2.7:
resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.3.1.tgz#78b8aa84878eb84dd29a51dcd664ded4cd0247c7" resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.3.1.tgz#78b8aa84878eb84dd29a51dcd664ded4cd0247c7"
integrity sha512-hxolIHCtQ/a56CUywaLzGD/V78zPwFihI+UK/4ZjOp7GoV4Mptmtv95yavOn/RlnTi7cCMjszvfcNrwCoWLH+Q== integrity sha512-hxolIHCtQ/a56CUywaLzGD/V78zPwFihI+UK/4ZjOp7GoV4Mptmtv95yavOn/RlnTi7cCMjszvfcNrwCoWLH+Q==
docker-compose@^0.23.8:
version "0.23.13"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.13.tgz#77d37bd05b6a966345f631e6d05e961c79514f06"
integrity sha512-/9fYC4g3AO+qsqxIZhmbVnFvJJPcYEV2yJbAPPXH+6AytU3urIY8lUAXOlvY8sl4u25pdKu1JrOfAmWC7lJDJg==
dependencies:
yaml "^1.10.2"
doctrine@^2.1.0: doctrine@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
...@@ -13963,7 +13948,7 @@ shebang-regex@^3.0.0: ...@@ -13963,7 +13948,7 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shelljs@^0.8.3, shelljs@^0.8.4: shelljs@^0.8.3:
version "0.8.5" version "0.8.5"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
...@@ -16639,7 +16624,7 @@ yallist@^4.0.0: ...@@ -16639,7 +16624,7 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^1.10.0, yaml@^1.10.2: yaml@^1.10.0:
version "1.10.2" version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
......
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