@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
281 lines (239 loc) • 6.62 kB
text/typescript
/* Imports: External */
import { Wallet, providers, BigNumber, utils } from 'ethers'
import { predeploys } from '@eth-optimism/contracts'
import { remove0x } from '@eth-optimism/core-utils'
import {
CrossChainMessenger,
NumberLike,
asL2Provider,
} from '@eth-optimism/sdk'
import { cleanEnv, str, num, bool, makeValidator } from 'envalid'
import dotenv from 'dotenv'
dotenv.config()
/* Imports: Internal */
import { OptimismEnv } from './env'
export const isLiveNetwork = () => {
return process.env.IS_LIVE_NETWORK === 'true'
}
export const HARDHAT_CHAIN_ID = 31337
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
}
return num()._parse(gasPrice).toString()
})
const addressValidator = makeValidator((addr) => {
if (!addr) {
return ''
} else if (utils.isAddress(addr)) {
return addr
} else {
throw new Error('Expected an address')
}
})
const procEnv = cleanEnv(process.env, {
L1_GAS_PRICE: gasPriceValidator({
default: '0',
}),
L1_URL: str({ default: 'http://localhost:9545' }),
L1_POLLING_INTERVAL: num({ default: 10 }),
L2_CHAINID: num({ default: 17 }),
L2_GAS_PRICE: gasPriceValidator({
default: 'onchain',
}),
L2_URL: str({ default: 'http://localhost:8545' }),
L2_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 }),
VERIFIER_URL: str({ default: 'http://localhost:8547' }),
HEALTHCHECK_URL: str({ default: 'http://localhost:7300/metrics' }),
PRIVATE_KEY: str({
default:
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
}),
GAS_PRICE_ORACLE_PRIVATE_KEY: str({
default:
'0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
}),
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_HEALTHCHECK_TESTS: bool({
default: true,
}),
RUN_DEBUG_TRACE_TESTS: bool({
default: true,
}),
RUN_STRESS_TESTS: bool({
default: true,
}),
RUN_VERIFIER_TESTS: bool({
default: true,
}),
RUN_SYSTEM_ADDRESS_TESTS: bool({
default: false,
}),
MOCHA_TIMEOUT: num({
default: 120_000,
}),
MOCHA_BAIL: bool({
default: false,
}),
BATCH_SUBMITTER_SEQUENCER_BATCH_TYPE: str({
default: 'zlib',
}),
ADDRESS_MANAGER: addressValidator({
default: '',
}),
L1_CROSS_DOMAIN_MESSENGER: addressValidator({
default: '',
}),
L1_STANDARD_BRIDGE: addressValidator({
default: '',
}),
STATE_COMMITMENT_CHAIN: addressValidator({
default: '',
}),
CANONICAL_TRANSACTION_CHAIN: addressValidator({
default: '',
}),
BOND_MANAGER: addressValidator({
default: '',
}),
})
export const envConfig = procEnv
// The hardhat instance
export const l1Provider = new providers.JsonRpcProvider(procEnv.L1_URL)
l1Provider.pollingInterval = procEnv.L1_POLLING_INTERVAL
export const l2Provider = asL2Provider(
new providers.JsonRpcProvider(procEnv.L2_URL)
)
l2Provider.pollingInterval = procEnv.L2_POLLING_INTERVAL
export const replicaProvider = asL2Provider(
new providers.JsonRpcProvider(procEnv.REPLICA_URL)
)
replicaProvider.pollingInterval = procEnv.REPLICA_POLLING_INTERVAL
export const verifierProvider = asL2Provider(
new providers.JsonRpcProvider(procEnv.VERIFIER_URL)
)
verifierProvider.pollingInterval = procEnv.L2_POLLING_INTERVAL
// The sequencer private key which is funded on L1
export const l1Wallet = new Wallet(procEnv.PRIVATE_KEY, l1Provider)
// A random private key which should always be funded with deposits from L1 -> L2
// if it's using non-0 gas price
export const l2Wallet = l1Wallet.connect(l2Provider)
// The owner of the GasPriceOracle on L2
export const gasPriceOracleWallet = new Wallet(
procEnv.GAS_PRICE_ORACLE_PRIVATE_KEY,
l2Provider
)
// Predeploys
export const OVM_ETH_ADDRESS = predeploys.OVM_ETH
export const L2_CHAINID = procEnv.L2_CHAINID
export const fundUser = async (
messenger: CrossChainMessenger,
amount: NumberLike,
recipient?: string
) => {
await messenger.waitForMessageReceipt(
await messenger.depositETH(amount, {
l2GasLimit: DEFAULT_TEST_GAS_L2,
overrides: {
gasPrice: DEFAULT_TEST_GAS_L1,
},
})
)
if (recipient !== undefined) {
const tx = await messenger.l2Signer.sendTransaction({
to: recipient,
value: amount,
})
await tx.wait()
}
}
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 * 2)
}
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.'
)
const abiCoder = new utils.AbiCoder()
export const encodeSolidityRevertMessage = (_reason: string): string => {
return '0x08c379a0' + remove0x(abiCoder.encode(['string'], [_reason]))
}
export const defaultTransactionFactory = () => {
return {
to: '0x' + '1234'.repeat(10),
gasLimit: 8_000_000,
gasPrice: BigNumber.from(0),
data: '0x',
value: 0,
}
}
export const gasPriceForL2 = async () => {
if (procEnv.L2_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
return l2Wallet.getGasPrice()
}
return utils.parseUnits(procEnv.L2_GAS_PRICE, 'wei')
}
export const gasPriceForL1 = async () => {
if (procEnv.L1_GAS_PRICE === ON_CHAIN_GAS_PRICE) {
return l1Wallet.getGasPrice()
}
return utils.parseUnits(procEnv.L1_GAS_PRICE, 'wei')
}
export const isHardhat = async () => {
const chainId = await l1Wallet.getChainId()
return chainId === HARDHAT_CHAIN_ID
}
export const die = (...args) => {
console.log(...args)
process.exit(1)
}
export const logStderr = (msg: string) => {
process.stderr.write(`${msg}\n`)
}