375 lines
10 KiB
TypeScript
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)
|
|
})
|
|
}
|