2022-12-09 18:11:04 +08:00

375 lines
10 KiB
TypeScript

require('dotenv').config()
import { ethers } from 'hardhat'
import { BigNumber, ContractFactory, Contract, Signer, providers } from 'ethers'
import {
getContractFactories,
readConfigFile,
updateConfigFile,
waitAfterTransaction,
wait,
doesNeedExplicitGasLimit,
Logger
} from '../shared/utils'
import {
isChainIdArbitrum,
isChainIdPolygon,
isChainIdOptimism
} from '../../config/utils'
import {
DEFAULT_DEADLINE,
ZERO_ADDRESS,
DEFAULT_ETHERS_OVERRIDES,
ALL_SUPPORTED_CHAIN_IDS
} from '../../config/constants'
const logger = Logger('setupL2')
let overrides: any = {}
interface Config {
l1ChainId: BigNumber
l2ChainId: BigNumber
l2CanonicalTokenAddress: string
l2HopBridgeTokenAddress: string
l2BridgeAddress: string
l2SwapAddress: string
liquidityProviderAmmAmount: BigNumber
l2CanonicalTokenIsEth: boolean
}
export async function setupL2 (config: Config) {
logger.log('setupL2')
let {
l1ChainId,
l2ChainId,
l2CanonicalTokenAddress,
l2HopBridgeTokenAddress,
l2BridgeAddress,
l2SwapAddress,
liquidityProviderAmmAmount,
l2CanonicalTokenIsEth
} = config
logger.log(`config:
l1ChainId: ${l1ChainId}
l2ChainId: ${l2ChainId}
l2CanonicalTokenAddress: ${l2CanonicalTokenAddress}
l2HopBridgeTokenAddress: ${l2HopBridgeTokenAddress}
l2BridgeAddress: ${l2BridgeAddress}
l2SwapAddress: ${l2SwapAddress}
liquidityProviderAmmAmount: ${liquidityProviderAmmAmount},
l2CanonicalTokenIsEth: ${l2CanonicalTokenIsEth}`
)
l1ChainId = BigNumber.from(l1ChainId)
l2ChainId = BigNumber.from(l2ChainId)
liquidityProviderAmmAmount = BigNumber.from(liquidityProviderAmmAmount)
// Signers
let accounts: Signer[]
let deployer: Signer
// Factories
let L2_MockERC20: ContractFactory
let L2_HopBridgeToken: ContractFactory
let L2_Bridge: ContractFactory
let L2_Swap: ContractFactory
// L2
let l2_canonicalToken: Contract
let l2_hopBridgeToken: Contract
let l2_bridge: Contract
let l2_swap: Contract
// Instantiate the wallets
accounts = await ethers.getSigners()
deployer = accounts[0]
logger.log('deployer:', await deployer.getAddress())
// Transaction
let tx: providers.TransactionResponse
logger.log('getting contract factories')
// Get the contract Factories
;({
L2_MockERC20,
L2_HopBridgeToken,
L2_Bridge,
L2_Swap
} = await getContractFactories(l2ChainId, deployer, ethers))
logger.log('attaching deployed contracts')
// Attach already deployed contracts
l2_canonicalToken = L2_MockERC20.attach(l2CanonicalTokenAddress)
l2_hopBridgeToken = L2_HopBridgeToken.attach(l2HopBridgeTokenAddress)
l2_bridge = L2_Bridge.attach(l2BridgeAddress)
l2_swap = L2_Swap.attach(l2SwapAddress)
if (!isChainIdOptimism(l2ChainId)) {
overrides = DEFAULT_ETHERS_OVERRIDES
}
/**
* Setup
*/
logger.log('waiting for L2 state verification')
logger.log(`verification parameters:
l2ChainId: ${l2ChainId}
l2CanonicalToken: ${l2_canonicalToken.address}
l2HopBridgeToken: ${l2_hopBridgeToken.address}
l2Bridge: ${l2_bridge.address}`)
// Some chains take a while to send state from L1 -> L2. Wait until the state have been fully sent.
await waitForL2StateVerification(
deployer,
l2_canonicalToken,
l2_hopBridgeToken,
l2_bridge,
l2CanonicalTokenIsEth
)
logger.log('L2 state verified')
// Set up Amm
if (l2CanonicalTokenIsEth) {
const gasLimit = DEFAULT_ETHERS_OVERRIDES.gasLimit
const gasPrice = DEFAULT_ETHERS_OVERRIDES.gasPrice
const depositTx = {
to: l2_canonicalToken.address,
value: liquidityProviderAmmAmount,
gasLimit,
gasPrice
}
tx = await deployer.sendTransaction(depositTx)
await tx.wait()
await waitAfterTransaction()
}
let approvalParams: any[] = [
l2_swap.address,
liquidityProviderAmmAmount
]
if (doesNeedExplicitGasLimit(l2ChainId)) {
approvalParams.push(overrides)
}
logger.log('approving L2 canonical token')
tx = await l2_canonicalToken
.connect(deployer)
.approve(...approvalParams)
await tx.wait()
await waitAfterTransaction()
logger.log('approving L2 hop bridge token')
tx = await l2_hopBridgeToken
.connect(deployer)
.approve(...approvalParams)
await tx.wait()
await waitAfterTransaction()
let addLiquidityParams: any[] = [
[liquidityProviderAmmAmount, liquidityProviderAmmAmount],
'0',
DEFAULT_DEADLINE
]
if (doesNeedExplicitGasLimit(l2ChainId)) {
addLiquidityParams.push(overrides)
} else if (isChainIdArbitrum(l2ChainId)) {
addLiquidityParams.push({ gasLimit: 100000000 })
}
logger.log('adding liquidity to L2 amm')
tx = await l2_swap
.connect(deployer)
.addLiquidity(...addLiquidityParams)
await tx.wait()
await waitAfterTransaction()
logger.log('retrieving lp token address')
const res = await l2_swap.swapStorage(overrides)
const lpTokenAddress = res[7]
const lpToken = L2_MockERC20.attach(lpTokenAddress)
const lpTokenAmount = await lpToken.balanceOf(
await deployer.getAddress(),
overrides
)
approvalParams = [
l2_swap.address,
lpTokenAmount
]
if (doesNeedExplicitGasLimit(l2ChainId)) {
approvalParams.push(overrides)
}
logger.log('approving L2 Swap LP token')
tx = await lpToken
.connect(deployer)
.approve(...approvalParams)
await tx.wait()
await waitAfterTransaction()
let removeLiquidityParams: any[] = [
lpTokenAmount,
['0', '0'],
DEFAULT_DEADLINE
]
if (doesNeedExplicitGasLimit(l2ChainId)) {
removeLiquidityParams.push(overrides)
} else if (isChainIdArbitrum(l2ChainId)) {
removeLiquidityParams.push({ gasLimit: 100000000 })
}
logger.log('removing liquidity from L2 amm')
tx = await l2_swap
.connect(deployer)
.removeLiquidity(...removeLiquidityParams)
await tx.wait()
await waitAfterTransaction()
updateConfigFile({
l2LpTokenAddress: lpTokenAddress
})
logger.log('L2 Setup Complete')
// Match output with addresses package
const postDeploymentAddresses = readConfigFile()
let l1FxBaseRootTunnel: string
let l2CanonicalBridgeAddress: string
if (isChainIdPolygon(l2ChainId)) {
l1FxBaseRootTunnel = postDeploymentAddresses.l1MessengerWrapperAddress
if (l2CanonicalTokenIsEth) {
const polygonMaticWithdrawalAddress = '0x0000000000000000000000000000000000001010'
l2CanonicalBridgeAddress = polygonMaticWithdrawalAddress
} else {
l2CanonicalBridgeAddress = postDeploymentAddresses.l2CanonicalTokenAddress
}
} else {
l1FxBaseRootTunnel = ''
l2CanonicalBridgeAddress = postDeploymentAddresses.l2TokenBridgeAddress
}
logger.log(`
l1Bridge: '${postDeploymentAddresses.l1BridgeAddress}'
l1MessengerWrapper: '${postDeploymentAddresses.l1MessengerWrapperAddress}',
l2CanonicalBridge: '${l2CanonicalBridgeAddress}',
l2CanonicalToken: '${postDeploymentAddresses.l2CanonicalTokenAddress}',
l2Bridge: '${postDeploymentAddresses.l2BridgeAddress}',
l2HopBridgeToken: '${postDeploymentAddresses.l2HopBridgeTokenAddress}',
l2AmmWrapper: '${postDeploymentAddresses.l2AmmWrapperAddress}',
l2SaddleSwap: '${postDeploymentAddresses.l2SwapAddress}',
l2SaddleLpToken: '${postDeploymentAddresses.l2LpTokenAddress}',
l1FxBaseRootTunnel: '${l1FxBaseRootTunnel}',
`)
}
const waitForL2StateVerification = async (
account: Signer,
l2_canonicalToken: Contract,
l2_hopBridgeToken: Contract,
l2_bridge: Contract,
l2CanonicalTokenIsEth: boolean
) => {
let checkCount: number = 0
let isStateSet: boolean = false
const supportedChainIds: BigNumber[] = ALL_SUPPORTED_CHAIN_IDS
while (!isStateSet) {
// Note: Mumbai can take up to 75 checks
if (checkCount === 75) {
throw new Error(
'L2 state has not been set after more than 10 minutes. Possibly due to a misconfiguration with modifiers on L2 bridge or messenger gas limit.'
)
}
// Validate that the chainIds have been added
let areChainIdsSupported: boolean = true
for (let i = 0; i < supportedChainIds.length; i++){
const isChainIdSupported: boolean = await l2_bridge.activeChainIds(
supportedChainIds[i],
overrides
)
if (!isChainIdSupported) {
areChainIdsSupported = false
}
}
// Validate that the Amm wrapper address has been set
const ammWrapperAddress: string = await l2_bridge.ammWrapper(
overrides
)
// Validate that the Hop Bridge Token balance has been updated
let canonicalTokenBalance: BigNumber
if (l2CanonicalTokenIsEth) {
canonicalTokenBalance = await account.getBalance()
} else {
canonicalTokenBalance = await l2_canonicalToken.balanceOf(
await account.getAddress(),
overrides
)
}
const hopBridgeTokenBalance: BigNumber = await l2_hopBridgeToken.balanceOf(
await account.getAddress(),
overrides
)
if (
!areChainIdsSupported ||
ammWrapperAddress === ZERO_ADDRESS ||
canonicalTokenBalance.eq(0) ||
hopBridgeTokenBalance.eq(0)
) {
logger.log('isChainIdSupported:', areChainIdsSupported)
logger.log('ammWrapperAddress:', ammWrapperAddress)
logger.log('canonicalTokenBalance:', canonicalTokenBalance.toString())
logger.log('hopBridgeTokenBalance:', hopBridgeTokenBalance.toString())
checkCount += 1
await wait(10e3)
} else {
logger.log('Number of iterations before state update:', checkCount)
isStateSet = true
}
}
return
}
if (require.main === module) {
const {
l1ChainId,
l2ChainId,
l2CanonicalTokenAddress,
l2HopBridgeTokenAddress,
l2BridgeAddress,
l2SwapAddress,
liquidityProviderAmmAmount,
l2CanonicalTokenIsEth
} = readConfigFile()
setupL2({
l1ChainId,
l2ChainId,
l2CanonicalTokenAddress,
l2HopBridgeTokenAddress,
l2BridgeAddress,
l2SwapAddress,
liquidityProviderAmmAmount,
l2CanonicalTokenIsEth
})
.then(() => {
process.exit(0)
})
.catch(error => {
logger.error(error)
process.exit(1)
})
}