Commit 8b1c6aa4 authored by Elena Gesheva's avatar Elena Gesheva Committed by GitHub

Merge branch 'develop' into run-dual-tests-synthetix

parents 39d22c85 f3938728
---
'@eth-optimism/integration-tests': patch
---
Updates to support nightly actor tests
---
'@eth-optimism/op-exporter': patch
---
Fixes panic caused by version initialized to nil
......@@ -20,6 +20,8 @@ import (
"k8s.io/client-go/kubernetes"
)
var UnknownStatus = "UNKNOWN"
var (
listenAddress = kingpin.Flag(
"web.listen-address",
......@@ -86,7 +88,7 @@ func main() {
healthy: false,
updateTime: time.Now(),
allowedMethods: nil,
version: nil,
version: &UnknownStatus,
}
http.Handle("/metrics", promhttp.Handler())
http.Handle("/health", healthHandler(&health))
......@@ -130,8 +132,7 @@ func getSequencerVersion(health *healthCheck, client *kubernetes.Clientset) {
}
sequencerStatefulSet, err := client.AppsV1().StatefulSets(string(ns)).Get(context.TODO(), "sequencer", getOpts)
if err != nil {
unknownStatus := "UNKNOWN"
health.version = &unknownStatus
health.version = &UnknownStatus
log.Errorf("Unable to retrieve a sequencer StatefulSet: %s", err)
continue
}
......
import fs from 'fs'
import client from 'prom-client'
import http from 'http'
import url from 'url'
export const metricsRegistry = new client.Registry()
......@@ -53,3 +55,18 @@ export const dumpMetrics = async (filename: string) => {
flag: 'w+',
})
}
export const serveMetrics = (port: number) => {
const server = http.createServer(async (req, res) => {
const route = url.parse(req.url).pathname
if (route !== '/metrics') {
res.writeHead(404)
res.end()
return
}
res.setHeader('Content-Type', metricsRegistry.contentType)
res.end(await metricsRegistry.metrics())
})
server.listen(port)
}
......@@ -3,7 +3,7 @@ import { defaultRuntime } from './convenience'
import { RunOpts } from './actor'
import { Command } from 'commander'
import pkg from '../../package.json'
import { metricsRegistry } from './metrics'
import { serveMetrics } from './metrics'
const program = new Command()
program.version(pkg.version)
......@@ -18,6 +18,11 @@ program
)
.option('-c, --concurrency <n>', 'number of concurrent workers to spawn', '1')
.option('--think-time <n>', 'how long to wait between each run', '0')
.option(
'-s, --serve [port]',
'Serve metrics with optional port number',
'8545'
)
program.parse(process.argv)
......@@ -27,6 +32,8 @@ const runsNum = Number(options.runs)
const timeNum = Number(options.time)
const concNum = Number(options.concurrency)
const thinkNum = Number(options.thinkTime)
const shouldServeMetrics = options.serve !== undefined
const metricsPort = options.serve || 8545
if (isNaN(runsNum) && isNaN(timeNum)) {
console.error('Must define either a number of runs or how long to run.')
......@@ -58,15 +65,19 @@ const opts: Partial<RunOpts> = {
runs: runsNum,
}
if (shouldServeMetrics) {
process.stderr.write(`Serving metrics on http://0.0.0.0:${metricsPort}.\n`)
serveMetrics(metricsPort)
}
defaultRuntime
.run(opts)
.then(() => metricsRegistry.metrics())
.then((metrics) => {
process.stderr.write('Run complete. Metrics:\n')
console.log(metrics)
.then(() => {
process.stderr.write('Run complete.\n')
process.exit(0)
})
.catch((err) => {
console.error('Error running:')
console.error('Error:')
console.error(err)
process.exit(1)
})
import { utils, Wallet, Contract, ContractFactory } from 'ethers'
import { utils, Wallet, Contract } from 'ethers'
import { actor, run, setupActor, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env'
import ERC721 from '../artifacts/contracts/NFT.sol/NFT.json'
......@@ -16,14 +16,7 @@ actor('NFT claimer', () => {
setupActor(async () => {
env = await OptimismEnv.new()
const factory = new ContractFactory(
ERC721.abi,
ERC721.bytecode,
env.l2Wallet
)
contract = await factory.deploy()
await contract.deployed()
contract = new Contract(process.env.ERC_721_ADDRESS, ERC721.abi)
})
setupRun(async () => {
......
import { BigNumber, Contract, utils, Wallet, ContractFactory } from 'ethers'
import { Contract, utils, Wallet } from 'ethers'
import { actor, run, setupActor, setupRun } from './lib/convenience'
import { OptimismEnv } from '../test/shared/env'
import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { FeeAmount } from '@uniswap/v3-sdk'
import ERC20 from '../artifacts/contracts/ERC20.sol/ERC20.json'
import { abi as NFTABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import { abi as RouterABI } from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'
interface Context {
contracts: { [name: string]: Contract }
wallet: Wallet
}
// Below methods taken from the Uniswap test suite, see
// https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts
export const getMinTick = (tickSpacing: number) =>
Math.ceil(-887272 / tickSpacing) * tickSpacing
export const getMaxTick = (tickSpacing: number) =>
Math.floor(887272 / tickSpacing) * tickSpacing
actor('Uniswap swapper', () => {
let env: OptimismEnv
......@@ -27,52 +21,29 @@ actor('Uniswap swapper', () => {
setupActor(async () => {
env = await OptimismEnv.new()
const factory = new ContractFactory(ERC20.abi, ERC20.bytecode, env.l2Wallet)
const tokenA = await factory.deploy(1000000000, 'OVM1', 8, 'OVM1')
await tokenA.deployed()
const tokenB = await factory.deploy(1000000000, 'OVM2', 8, 'OVM2')
await tokenB.deployed()
tokens =
tokenA.address < tokenB.address ? [tokenA, tokenB] : [tokenB, tokenA]
contracts = await UniswapV3Deployer.deploy(env.l2Wallet)
let tx
for (const token of tokens) {
tx = await token.approve(contracts.positionManager.address, 1000000000)
await tx.wait()
tx = await token.approve(contracts.router.address, 1000000000)
await tx.wait()
contracts = {
positionManager: new Contract(
process.env.UNISWAP_POSITION_MANAGER_ADDRESS,
NFTABI
).connect(env.l2Wallet),
router: new Contract(
process.env.UNISWAP_ROUTER_ADDRESS,
RouterABI
).connect(env.l2Wallet),
}
tx = await contracts.positionManager.createAndInitializePoolIfNecessary(
tokens[0].address,
tokens[1].address,
FeeAmount.MEDIUM,
// initial ratio of 1/1
BigNumber.from('79228162514264337593543950336')
)
await tx.wait()
tx = await contracts.positionManager.mint(
{
token0: tokens[0].address,
token1: tokens[1].address,
tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
fee: FeeAmount.MEDIUM,
recipient: env.l2Wallet.address,
amount0Desired: 100000000,
amount1Desired: 100000000,
amount0Min: 0,
amount1Min: 0,
deadline: Date.now() * 2,
},
{
gasLimit: 10000000,
}
)
await tx.wait()
tokens = [
new Contract(process.env.UNISWAP_TOKEN_0_ADDRESS, ERC20.abi).connect(
env.l2Wallet
),
new Contract(process.env.UNISWAP_TOKEN_1_ADDRESS, ERC20.abi).connect(
env.l2Wallet
),
]
tokens =
tokens[0].address.toLowerCase() < tokens[1].address.toLowerCase()
? [tokens[0], tokens[1]]
: [tokens[1], tokens[0]]
})
setupRun(async () => {
......
......@@ -8,6 +8,7 @@
"sync-tests/*.ts",
"./actor-tests/**/*.ts",
"./artifacts/**/*.json",
"./tasks/**/*.ts",
"./package.json"
],
"files": ["./hardhat.config.ts"]
......
import { Signer, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider'
import { getContractArtifact } from './contract-artifacts'
import { getDeployedContractArtifact } from './contract-deployed-artifacts'
import { predeploys } from './predeploys'
export type Network = 'goerli' | 'kovan' | 'mainnet'
interface L1Contracts {
addressManager: Contract
canonicalTransactionChain: Contract
stateCommitmentChain: Contract
xDomainMessengerProxy: Contract
bondManager: Contract
}
interface L2Contracts {
eth: Contract
xDomainMessenger: Contract
messagePasser: Contract
deployerWhiteList: Contract
}
/**
* Validates user provided a singer or provider & throws error if not
*
* @param signerOrProvider
*/
const checkSignerType = (signerOrProvider: Signer | Provider) => {
if (!signerOrProvider) {
throw Error('signerOrProvider argument is undefined')
}
if (
!Provider.isProvider(signerOrProvider) &&
!Signer.isSigner(signerOrProvider)
) {
throw Error('signerOrProvider argument is the wrong type')
}
}
/**
* Connects a signer/provider to layer 1 contracts on a given network
*
* @param signerOrProvider ethers signer or provider
* @param network string denoting network
* @returns l1 contracts connected to signer/provider
*/
export const connectL1Contracts = async (
signerOrProvider: Signer | Provider,
network: Network
): Promise<L1Contracts> => {
checkSignerType(signerOrProvider)
if (!['mainnet', 'kovan', 'goerli'].includes(network)) {
throw Error('Must specify network: mainnet, kovan, or goerli.')
}
const getEthersContract = (name: string) => {
const artifact = getDeployedContractArtifact(name, network)
return new Contract(artifact.address, artifact.abi, signerOrProvider)
}
return {
addressManager: getEthersContract('Lib_AddressManager'),
canonicalTransactionChain: getEthersContract('CanonicalTransactionChain'),
stateCommitmentChain: getEthersContract('StateCommitmentChain'),
xDomainMessengerProxy: getEthersContract('Proxy__L1CrossDomainMessenger'),
bondManager: getEthersContract('mockBondManager'),
}
}
/**
* Connects a signer/provider to layer 2 contracts (network agnostic)
*
* @param signerOrProvider ethers signer or provider
* @returns l2 contracts connected to signer/provider
*/
export const connectL2Contracts = async (
signerOrProvider: any
): Promise<L2Contracts> => {
checkSignerType(signerOrProvider)
const getEthersContract = (name: string, iface?: string) => {
const artifact = getContractArtifact(iface || name)
const address = predeploys[name]
return new Contract(address, artifact.abi, signerOrProvider)
}
return {
eth: getEthersContract('OVM_ETH'),
xDomainMessenger: getEthersContract('L2CrossDomainMessenger'),
messagePasser: getEthersContract('OVM_L2ToL1MessagePasser'),
deployerWhiteList: getEthersContract('OVM_DeployerWhitelist'),
}
}
/* eslint-disable @typescript-eslint/no-var-requires */
import { predeploys as l2Addresses } from './predeploys'
import { Network } from './connect-contracts'
/**
* This file is necessarily not DRY because it needs to be usable
* in a browser context and can't take advantage of dynamic imports
* (ie: the json needs to all be imported when transpiled)
*/
const Mainnet__Lib_AddressManager = require('../deployments/mainnet/Lib_AddressManager.json')
const Mainnet__CanonicalTransactionChain = require('../deployments/mainnet/CanonicalTransactionChain.json')
const Mainnet__L1CrossDomainMessenger = require('../deployments/mainnet/L1CrossDomainMessenger.json')
const Mainnet__StateCommitmentChain = require('../deployments/mainnet/StateCommitmentChain.json')
const Mainnet__Proxy__L1CrossDomainMessenger = require('../deployments/mainnet/Proxy__L1CrossDomainMessenger.json')
const Mainnet__BondManager = require('../deployments/mainnet/mockBondManager.json')
const Kovan__Lib_AddressManager = require('../deployments/kovan/Lib_AddressManager.json')
const Kovan__CanonicalTransactionChain = require('../deployments/kovan/CanonicalTransactionChain.json')
const Kovan__L1CrossDomainMessenger = require('../deployments/kovan/L1CrossDomainMessenger.json')
const Kovan__StateCommitmentChain = require('../deployments/kovan/StateCommitmentChain.json')
const Kovan__Proxy__L1CrossDomainMessenger = require('../deployments/kovan/Proxy__L1CrossDomainMessenger.json')
const Kovan__BondManager = require('../deployments/kovan/mockBondManager.json')
const Goerli__Lib_AddressManager = require('../deployments/goerli/Lib_AddressManager.json')
const Goerli__CanonicalTransactionChain = require('../deployments/goerli/CanonicalTransactionChain.json')
const Goerli__L1CrossDomainMessenger = require('../deployments/goerli/L1CrossDomainMessenger.json')
const Goerli__StateCommitmentChain = require('../deployments/goerli/StateCommitmentChain.json')
const Goerli__Proxy__L1CrossDomainMessenger = require('../deployments/goerli/Proxy__L1CrossDomainMessenger.json')
const Goerli__BondManager = require('../deployments/goerli/mockBondManager.json')
export const getL1ContractData = (network: Network) => {
return {
Lib_AddressManager: {
mainnet: Mainnet__Lib_AddressManager,
kovan: Kovan__Lib_AddressManager,
goerli: Goerli__Lib_AddressManager,
}[network],
CanonicalTransactionChain: {
mainnet: Mainnet__CanonicalTransactionChain,
kovan: Kovan__CanonicalTransactionChain,
goerli: Goerli__CanonicalTransactionChain,
}[network],
L1CrossDomainMessenger: {
mainnet: Mainnet__L1CrossDomainMessenger,
kovan: Kovan__L1CrossDomainMessenger,
goerli: Goerli__L1CrossDomainMessenger,
}[network],
StateCommitmentChain: {
mainnet: Mainnet__StateCommitmentChain,
kovan: Kovan__StateCommitmentChain,
goerli: Goerli__StateCommitmentChain,
}[network],
Proxy__L1CrossDomainMessenger: {
mainnet: Mainnet__Proxy__L1CrossDomainMessenger,
kovan: Kovan__Proxy__L1CrossDomainMessenger,
goerli: Goerli__Proxy__L1CrossDomainMessenger,
}[network],
BondManager: {
mainnet: Mainnet__BondManager,
kovan: Kovan__BondManager,
goerli: Goerli__BondManager,
}[network],
}
}
const OVM_ETH = require('../artifacts/contracts/L2/predeploys/OVM_ETH.sol/OVM_ETH.json')
const L2CrossDomainMessenger = require('../artifacts/contracts/L2/messaging/L2CrossDomainMessenger.sol/L2CrossDomainMessenger.json')
const OVM_L2ToL1MessagePasser = require('../artifacts/contracts/L2/predeploys/OVM_L2ToL1MessagePasser.sol/OVM_L2ToL1MessagePasser.json')
const OVM_DeployerWhitelist = require('../artifacts/contracts/L2/predeploys/OVM_DeployerWhitelist.sol/OVM_DeployerWhitelist.json')
export const getL2ContractData = () => {
return {
OVM_ETH: {
abi: OVM_ETH.abi,
address: l2Addresses.OVM_ETH,
},
L2CrossDomainMessenger: {
abi: L2CrossDomainMessenger.abi,
address: l2Addresses.L2CrossDomainMessenger,
},
OVM_L2ToL1MessagePasser: {
abi: OVM_L2ToL1MessagePasser.abi,
address: l2Addresses.OVM_L2ToL1MessagePasser,
},
OVM_DeployerWhitelist: {
abi: OVM_DeployerWhitelist.abi,
address: l2Addresses.OVM_DeployerWhitelist,
},
}
}
export * from './contract-defs'
export * from './predeploys'
export * from './connect-contracts'
import { ethers } from 'hardhat'
import { Signer, Contract } from 'ethers'
import {
connectL1Contracts,
connectL2Contracts,
} from '../dist/connect-contracts'
import { expect } from './setup'
// Skipping these tests as the FE work that relies on this logic was never finished.
// Dedicated issue created in https://linear.app/optimism/issue/ENG-1451/decide-what-to-do-with-the-connectl1contracts-tests
describe.skip('connectL1Contracts', () => {
let user: Signer
const l1ContractNames = [
'addressManager',
'canonicalTransactionChain',
'stateCommitmentChain',
'xDomainMessengerProxy',
'bondManager',
]
const l2ContractNames = [
'eth',
'xDomainMessenger',
'messagePasser',
'messageSender',
'deployerWhiteList',
]
before(async () => {
;[user] = await ethers.getSigners()
})
it(`connectL1Contracts should throw error if signer or provider isn't provided.`, async () => {
try {
await connectL1Contracts(undefined, 'mainnet')
} catch (err) {
expect(err.message).to.be.equal('signerOrProvider argument is undefined')
}
})
for (const name of l1ContractNames) {
it(`connectL1Contracts should return a contract assigned to a field named "${name}"`, async () => {
const l1Contracts = await connectL1Contracts(user, 'mainnet')
expect(l1Contracts[name]).to.be.an.instanceOf(Contract)
})
}
for (const name of l2ContractNames) {
it(`connectL2Contracts should return a contract assigned to a field named "${name}"`, async () => {
const l2Contracts = await connectL2Contracts(user)
expect(l2Contracts[name]).to.be.an.instanceOf(Contract)
})
}
})
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