import { BigNumber, ethers, Wallet } from 'ethers' import { defaultAbiCoder } from 'ethers/lib/utils' import { waitForTx, waitToRelayTxsToL2 } from '../arbitrum-helpers' import { L1DaiGateway } from '../typechain' import { getArbitrumCoreContracts } from './contracts' import { BridgeDeployment, NetworkConfig } from './deploy' export async function getGasPriceBid(l2: ethers.providers.BaseProvider): Promise<BigNumber> { return await l2.getGasPrice() } export async function getMaxSubmissionPrice( l2: ethers.providers.BaseProvider, calldataOrCalldataLength: string | number, ) { const calldataLength = typeof calldataOrCalldataLength === 'string' ? calldataOrCalldataLength.length : calldataOrCalldataLength const [submissionPrice] = await getArbitrumCoreContracts(l2).arbRetryableTx.getSubmissionPrice(calldataLength) const maxSubmissionPrice = submissionPrice.mul(4) return maxSubmissionPrice } export async function getMaxGas( l2: ethers.providers.BaseProvider, sender: string, destination: string, refundDestination: string, maxSubmissionPrice: BigNumber, gasPriceBid: BigNumber, calldata: string, ): Promise<BigNumber> { const [estimatedGas] = await getArbitrumCoreContracts(l2).nodeInterface.estimateRetryableTicket( sender, ethers.utils.parseEther('0.05'), destination, 0, maxSubmissionPrice, refundDestination, refundDestination, 0, gasPriceBid, calldata, ) const maxGas = estimatedGas.mul(4) return maxGas } export async function depositToStandardBridge({ from, to, l2Provider, deposit, l1Gateway, l1TokenAddress, l2GatewayAddress, }: { from: Wallet to: string l2Provider: ethers.providers.BaseProvider deposit: BigNumber | string l1Gateway: L1DaiGateway l1TokenAddress: string l2GatewayAddress: string }) { const gasPriceBid = await getGasPriceBid(l2Provider) const onlyData = '0x' const depositCalldata = await l1Gateway.getOutboundCalldata(l1TokenAddress, from.address, to, deposit, onlyData) const maxSubmissionPrice = await getMaxSubmissionPrice(l2Provider, depositCalldata) const maxGas = await getMaxGas( l2Provider, l1Gateway.address, l2GatewayAddress, from.address, maxSubmissionPrice, gasPriceBid, depositCalldata, ) const defaultData = defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionPrice, onlyData]) const ethValue = await maxSubmissionPrice.add(gasPriceBid.mul(maxGas)) return await waitForTx( l1Gateway.connect(from).outboundTransfer(l1TokenAddress, to, deposit, maxGas, gasPriceBid, defaultData, { value: ethValue, }), ) } export async function depositToStandardRouter({ from, to, l2Provider, deposit, l1Gateway, l1Router, l1TokenAddress, l2GatewayAddress, }: { from: Wallet to: string l2Provider: ethers.providers.BaseProvider deposit: BigNumber | string l1Router: any l1Gateway: L1DaiGateway l1TokenAddress: string l2GatewayAddress: string }) { const gasPriceBid = await getGasPriceBid(l2Provider) const onlyData = '0x' const depositCalldata = await l1Gateway.getOutboundCalldata(l1TokenAddress, from.address, to, deposit, onlyData) const maxSubmissionPrice = await getMaxSubmissionPrice(l2Provider, depositCalldata) const maxGas = await getMaxGas( l2Provider, l1Gateway.address, l2GatewayAddress, from.address, maxSubmissionPrice, gasPriceBid, depositCalldata, ) const defaultData = defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionPrice, onlyData]) const ethValue = await maxSubmissionPrice.add(gasPriceBid.mul(maxGas)) return await waitForTx( l1Router.connect(from).outboundTransfer(l1TokenAddress, to, deposit, maxGas, gasPriceBid, defaultData, { value: ethValue, }), ) } export async function setGatewayForToken({ l2Provider, l1Router, tokenGateway, }: { l2Provider: ethers.providers.BaseProvider l1Router: any l2Router: any tokenGateway: L1DaiGateway }) { const token = await tokenGateway.l1Dai() const calldataLength = 300 + 20 * 2 // fixedOverheadLength + 2 * address const gasPriceBid = await getGasPriceBid(l2Provider) const maxSubmissionPrice = await getMaxSubmissionPrice(l2Provider, calldataLength) await l1Router.setGateways([token], [tokenGateway.address], 0, gasPriceBid, maxSubmissionPrice, { value: maxSubmissionPrice, }) } export async function executeSpell( network: NetworkConfig, bridgeDeployment: BridgeDeployment, l2Spell: string, spellCalldata: string, ) { const l2MessageCalldata = bridgeDeployment.l2GovRelay.interface.encodeFunctionData('relay', [l2Spell, spellCalldata]) const calldataLength = l2MessageCalldata.length const gasPriceBid = await getGasPriceBid(network.l2.provider) const maxSubmissionPrice = await getMaxSubmissionPrice(network.l2.provider, calldataLength) const maxGas = await getMaxGas( network.l2.provider, bridgeDeployment.l1GovRelay.address, bridgeDeployment.l2GovRelay.address, bridgeDeployment.l2GovRelay.address, maxSubmissionPrice, gasPriceBid, l2MessageCalldata, ) const ethValue = maxSubmissionPrice.add(gasPriceBid.mul(maxGas)) console.log('ethValue: ', ethValue.toString()) await network.l1.deployer.sendTransaction({ to: bridgeDeployment.l1GovRelay.address, value: ethValue }) await waitToRelayTxsToL2( waitForTx( bridgeDeployment.l1GovRelay .connect(network.l1.deployer) .relay(l2Spell, spellCalldata, ethValue, maxGas, gasPriceBid, maxSubmissionPrice), ), network.l1.inbox, network.l1.provider, network.l2.provider, ) }