2022-12-09 18:15:16 +08:00

454 lines
12 KiB
TypeScript

import path from 'path'
import { spawn } from 'child_process'
import fs from 'fs'
import {
ContractFactory,
Contract,
BigNumber,
Signer,
utils as ethersUtils
} from 'ethers'
import {
isChainIdOptimism,
isChainIdArbitrum,
isChainIdXDai,
isChainIdPolygon,
isChainIdMainnet
} from '../../config/utils'
import {
CHAIN_IDS,
GAS_PRICE_MULTIPLIERS,
ZERO_ADDRESS,
LIQUIDITY_PROVIDER_INITIAL_BALANCE,
COMMON_SYMBOLS
} from '../../config/constants'
import {
mainnetNetworkData,
goerliNetworkData,
kovanNetworkData,
localNetworkData
} from '../../config/networks/index'
export const getContractFactories = async (
chainId: BigNumber,
signer: Signer,
ethers: any
) => {
const L1_MockERC20: ContractFactory = await ethers.getContractFactory(
'contracts/test/MockERC20.sol:MockERC20',
{ signer }
)
const L1_Bridge: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/L1_ERC20_Bridge.sol:L1_ERC20_Bridge',
{ signer }
)
const L2_MockERC20: ContractFactory = await ethers.getContractFactory(
'contracts/test/MockERC20.sol:MockERC20',
{ signer }
)
const L2_HopBridgeToken: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/HopBridgeToken.sol:HopBridgeToken',
{ signer }
)
// This contract needs to be linked. This line links it to an arbitrary address.
// The linking must only be done for the deployment of this contract. Rather than doing it in this getter function,
// it is done in the appropriate location in code. This is because the linked libraries are not deployed sometimes
// when this function is called.
const L2_Swap: ContractFactory = await ethers.getContractFactory(
'Swap',
{
signer,
libraries: {
'SwapUtils': ZERO_ADDRESS
}
}
)
const L2_AmmWrapper: ContractFactory = await ethers.getContractFactory('L2_AmmWrapper', { signer })
let L1_TokenBridge: ContractFactory
let L1_Messenger: ContractFactory
let L1_MessengerWrapper: ContractFactory
let L2_Bridge: ContractFactory
;({
L1_TokenBridge,
L1_Messenger,
L1_MessengerWrapper,
L2_Bridge
} = await getNetworkSpecificFactories(chainId, signer, ethers))
return {
L1_MockERC20,
L1_TokenBridge,
L1_Bridge,
L1_MessengerWrapper,
L1_Messenger,
L2_MockERC20,
L2_HopBridgeToken,
L2_Bridge,
L2_Swap,
L2_AmmWrapper
}
}
const getNetworkSpecificFactories = async (
chainId: BigNumber,
signer: Signer,
ethers: any
) => {
if (isChainIdOptimism(chainId)) {
return getOptimismContractFactories(signer, ethers)
} else if (isChainIdArbitrum(chainId)) {
return getArbitrumContractFactories(signer, ethers)
} else if (isChainIdXDai(chainId)) {
return getXDaiContractFactories(signer, ethers)
} else if (isChainIdPolygon(chainId)) {
return getPolygonContractFactories(signer, ethers)
} else {
return {
L1_TokenBridge: null,
L1_Messenger: null,
L1_MessengerWrapper: null,
L2_Bridge: null
}
}
}
const getOptimismContractFactories = async (
signer: Signer,
ethers: any
) => {
const L1_TokenBridge: ContractFactory = await ethers.getContractFactory(
'contracts/test/optimism/mockOVM_L1StandardBridge.sol:mockOVM_L1StandardBridge',
{ signer }
)
const L1_Messenger: ContractFactory = await ethers.getContractFactory(
'contracts/test/optimism/mockOVM_CrossDomainMessenger.sol:mockOVM_CrossDomainMessenger',
{ signer }
)
const L1_MessengerWrapper: ContractFactory = await ethers.getContractFactory(
'contracts/wrappers/OptimismMessengerWrapper.sol:OptimismMessengerWrapper',
{ signer }
)
const L2_Bridge: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/L2_OptimismBridge.sol:L2_OptimismBridge',
{ signer }
)
return {
L1_TokenBridge,
L1_Messenger,
L1_MessengerWrapper,
L2_Bridge
}
}
const getArbitrumContractFactories = async (signer: Signer, ethers: any) => {
const L1_TokenBridge: ContractFactory = await ethers.getContractFactory(
'contracts/interfaces/arbitrum/bridges/IEthERC20Bridge.sol:IEthERC20Bridge',
{ signer }
)
const L1_Messenger: ContractFactory = await ethers.getContractFactory(
'contracts/interfaces/arbitrum/messengers/IInbox.sol:IInbox',
{ signer }
)
const L1_MessengerWrapper: ContractFactory = await ethers.getContractFactory(
'contracts/wrappers/ArbitrumMessengerWrapper.sol:ArbitrumMessengerWrapper',
{ signer }
)
const L2_Bridge: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/L2_ArbitrumBridge.sol:L2_ArbitrumBridge',
{ signer }
)
return {
L1_TokenBridge,
L1_Messenger,
L1_MessengerWrapper,
L2_Bridge
}
}
const getXDaiContractFactories = async (signer: Signer, ethers: any) => {
const L1_TokenBridge: ContractFactory = await ethers.getContractFactory(
'contracts/test/xDai/MockForeignOmniBridge.sol:MockForeignOmniBridge',
{ signer }
)
const L1_Messenger: ContractFactory = await ethers.getContractFactory(
'contracts/test/xDai/Mock_L1_xDaiMessenger.sol:Mock_L1_xDaiMessenger',
{ signer }
)
const L1_MessengerWrapper: ContractFactory = await ethers.getContractFactory(
'contracts/wrappers/XDaiMessengerWrapper.sol:XDaiMessengerWrapper',
{ signer }
)
const L2_Bridge: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/L2_XDaiBridge.sol:L2_XDaiBridge',
{ signer }
)
return {
L1_TokenBridge,
L1_Messenger,
L1_MessengerWrapper,
L2_Bridge
}
}
const getPolygonContractFactories = async (signer: Signer, ethers: any) => {
const L1_TokenBridge: ContractFactory = await ethers.getContractFactory(
'contracts/test/polygon/MockRootChainManager.sol:MockRootChainManager',
{ signer }
)
const L1_Messenger: ContractFactory = await ethers.getContractFactory(
'contracts/test/polygon/Mock_L1_PolygonMessenger.sol:Mock_L1_PolygonMessenger',
{ signer }
)
const L1_MessengerWrapper: ContractFactory = await ethers.getContractFactory(
'contracts/wrappers/PolygonMessengerWrapper.sol:PolygonMessengerWrapper',
{ signer }
)
const L2_Bridge: ContractFactory = await ethers.getContractFactory(
'contracts/bridges/L2_PolygonBridge.sol:L2_PolygonBridge',
{ signer }
)
return {
L1_TokenBridge,
L1_Messenger,
L1_MessengerWrapper,
L2_Bridge
}
}
export const sendChainSpecificBridgeDeposit = async (
chainId: BigNumber,
sender: Signer,
amount: BigNumber,
l1_tokenBridge: Contract,
l1_canonicalToken: Contract,
l2_canonicalToken: Contract,
modifiedGasPrice: { [key: string]: BigNumber } | undefined = undefined
) => {
let tx
modifiedGasPrice = modifiedGasPrice || {}
if (isChainIdOptimism(chainId)) {
const l2GasForTransfer = BigNumber.from('2000000')
const calldata = '0x'
tx = await l1_tokenBridge
.connect(sender)
.deposit(
l1_canonicalToken.address,
l2_canonicalToken.address,
amount,
l2GasForTransfer,
calldata,
modifiedGasPrice
)
} else if (isChainIdArbitrum(chainId)) {
tx = await l1_tokenBridge
.connect(sender)
.depositAsERC20(
l1_canonicalToken.address,
await sender.getAddress(),
amount,
'0',
'1000000000000000000',
'0',
'0x',
modifiedGasPrice
)
} else if (isChainIdXDai(chainId)) {
tx = await l1_tokenBridge
.connect(sender)
.relayTokens(
l1_canonicalToken.address,
await sender.getAddress(),
amount,
modifiedGasPrice
)
} else if (isChainIdPolygon(chainId)) {
const encodedAmount = ethersUtils.defaultAbiCoder.encode(['uint256'], [amount])
tx = await l1_tokenBridge
.connect(sender)
.depositFor(
await sender.getAddress(),
l1_canonicalToken.address,
encodedAmount,
modifiedGasPrice
)
} else {
throw new Error(`Unsupported chain ID "${chainId}"`)
}
await tx.wait()
}
const configFilepath = path.resolve(__dirname, '../deployAndSetupHop/deploy_config.json')
export const updateConfigFile = (newData: any) => {
const data = readConfigFile()
fs.writeFileSync(
configFilepath,
JSON.stringify({ ...data, ...newData }, null, 2)
)
}
export const readConfigFile = () => {
let data: any = {
l1ChainId: '',
l2ChainId: '',
l1CanonicalTokenAddress: '',
l1MessengerAddress: '',
l1BridgeAddress: '',
l2BridgeAddress: '',
l2CanonicalTokenAddress: '',
l2HopBridgeTokenAddress: '',
l2MessengerAddress: '',
l2SwapAddress: '',
l2AmmWrapperAddress: '',
l2SwapLpTokenName: '',
l2SwapLpTokenSymbol: '',
bonderAddress: ''
}
if (fs.existsSync(configFilepath)) {
data = JSON.parse(fs.readFileSync(configFilepath, 'utf8'))
}
return data
}
export const getNetworkDataByNetworkName = (networkName: string) => {
if (networkName === 'mainnet') {
return mainnetNetworkData
} else if (networkName === 'goerli') {
return goerliNetworkData
} else if (networkName === 'kovan') {
return kovanNetworkData
} else if (networkName === 'local' || networkName === 'local_l1') {
return localNetworkData
} else{
console.log('networkname:', networkName)
throw new Error('Invalid network name')
}
}
export const waitAfterTransaction = async (
contract: Contract = null,
ethers = null
) => {
// Ethers does not wait long enough after `deployed()` on some networks
// so we wait additional time to verify deployment
if (contract) {
await contract.deployed()
}
// NOTE: 6 seconds seems to work fine. 5 seconds does not always work
const secondsToWait = 6e3
await wait(secondsToWait)
if (contract && ethers) {
await verifyDeployment(contract, ethers)
}
}
export const verifyDeployment = async (contract: Contract, ethers) => {
let addr = contract.address
console.log('[verifyDeployment contract]start:', addr)
const isCodeAtAddress =
(await ethers.provider.getCode(contract.address)).length > 50
if (!isCodeAtAddress) {
throw new Error('Did not deploy correctly')
}else{
console.log('[verifyDeployment contract]ok:', addr)
}
}
export const wait = async (t: number) => {
return new Promise(resolve => setTimeout(() => resolve(null), t))
}
export async function execScript (cmd: string) {
return new Promise((resolve, reject) => {
const parts = cmd.split(' ')
const proc = spawn(parts[0], parts.slice(1))
proc.stdout.on('data', data => {
process.stdout.write(data.toString())
})
proc.stderr.on('data', data => {
process.stderr.write(data.toString())
})
proc.on('exit', code => {
if (code !== 0) {
reject(code)
return
}
resolve(code)
})
})
}
export const Logger = (label: string) => {
label = `[${label}]`
return {
log: (...args: any[]) => {
console.log(label, getTimestamp(), ...args)
},
error: (...args: any[]) => {
console.error(label, getTimestamp(), ...args)
}
}
}
const getTimestamp = (): string => {
let timestamp: string = new Date(Date.now()).toISOString().substr(11, 8)
return `[${timestamp}]`
}
export const getL1ChainIdFromNetworkName = (networkName: string): BigNumber => {
return CHAIN_IDS.ETHEREUM[networkName.toUpperCase()]
}
export const doesNeedExplicitGasLimit = (chainId: BigNumber): Boolean => {
if (isChainIdXDai(chainId) || isChainIdPolygon(chainId)) {
return true
}
return false
}
export const getTokenSymbolLetterCase = (tokenSymbol: string): string => {
tokenSymbol = tokenSymbol.toLowerCase()
if (tokenSymbol.toLowerCase() === 'dai') return 'DAI'
else if (tokenSymbol.toLowerCase() === 'seth') return 'sETh'
else if (tokenSymbol.toLowerCase() === 'sbtc') return 'sBTC'
else if (tokenSymbol.toLowerCase() === 'usdc') return 'USDC'
else if (tokenSymbol.toLowerCase() === 'wbtc') return 'WBTC'
else if (tokenSymbol.toLowerCase() === 'usdt') return 'USDT'
else if (tokenSymbol.toLowerCase() === 'matic') return 'MATIC'
else {
throw new Error ('Invalid token symbol getter')
}
}
export const getModifiedGasPrice = async (ethers, l1ChainId: BigNumber) => {
let gasPriceMultiplier: number
if (isChainIdMainnet(l1ChainId)) {
gasPriceMultiplier = GAS_PRICE_MULTIPLIERS.MAINNET
} else {
gasPriceMultiplier = GAS_PRICE_MULTIPLIERS.TESTNET
}
const tempMultiplier = 100
const wholeGasPriceMultiplier = gasPriceMultiplier * tempMultiplier
const gasPrice: BigNumber = (await ethers.provider.getGasPrice()).mul(wholeGasPriceMultiplier).div(tempMultiplier)
return {
gasPrice
}
}