Skip to content

Commit

Permalink
feat: add scripts-altlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
antonio-altr committed Mar 25, 2024
1 parent d9f1f74 commit dfb9b71
Show file tree
Hide file tree
Showing 14 changed files with 1,035 additions and 0 deletions.
25 changes: 25 additions & 0 deletions scripts-altlayer/checkOwners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { abi as ArbOwner__abi } from '@arbitrum/nitro-contracts/build/contracts/src/precompiles/ArbOwner.sol/ArbOwner.json'
import { ethers } from 'ethers'

export async function main() {
const privateKey = process.env.PRIVATE_KEY || ''
const L3_RPC_URL = process.env.L3_RPC_URL || ''
const L3Provider = new ethers.providers.JsonRpcProvider(L3_RPC_URL)

// Creating the wallet and signer
const l3signer = new ethers.Wallet(privateKey).connect(L3Provider)

// Arb Owner precompile address
const arbOwnerAddress = '0x000000000000000000000000000000000000006b'

// Constructing call data for setting L1 basefee on L3
const ArbOwner = new ethers.Contract(arbOwnerAddress, ArbOwner__abi, l3signer)
const owners = await ArbOwner.getAllChainOwners()
console.log('chain owners: ', owners)
}

// Run the script
main().catch(error => {
console.error(error)
process.exit(1)
})
49 changes: 49 additions & 0 deletions scripts-altlayer/checkRoles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ethers } from 'ethers'
import UpgradeExecutor from '@arbitrum/nitro-contracts/build/contracts/src/mocks/UpgradeExecutorMock.sol/UpgradeExecutorMock.json'
import { getSigner } from './erc20TokenBridgeDeployment'
import { getExecutorAddress } from './getExecutorAddress'

async function main() {
// Read the environment variables
const privateKey = process.env.PRIVATE_KEY
const TOKEN_BRIDGE_CREATOR = process.env.TOKEN_BRIDGE_CREATOR || ''
const L2_RPC_URL = process.env.L2_RPC_URL
const L3_RPC_URL = process.env.L3_RPC_URL
const OPERATOR = process.env.OPERATOR
const INBOX = process.env.INBOX || ''
if (!privateKey || !L3_RPC_URL || !OPERATOR) {
throw new Error('Required environment variable not found')
}

// Generating providers from RPCs
const L2Provider = new ethers.providers.JsonRpcProvider(L2_RPC_URL)
const L3Provider = new ethers.providers.JsonRpcProvider(L3_RPC_URL)
const l3Deployer = getSigner(L3Provider, privateKey)

const executorContractAddress = await getExecutorAddress(
TOKEN_BRIDGE_CREATOR,
INBOX,
L2Provider,
L3Provider
)

console.log('executor address: ', executorContractAddress)
//Defining upgrade executor contract
const executorContract__factory = new ethers.Contract(
executorContractAddress,
UpgradeExecutor.abi,
l3Deployer
)
const upgradeExecutor = executorContract__factory.connect(l3Deployer)
// const adminRole = await upgradeExecutor.ADMIN_ROLE()
const executorRole = await upgradeExecutor.EXECUTOR_ROLE()

const hasExecutorRole = await upgradeExecutor.hasRole(executorRole, OPERATOR)
console.log('has executor role: ', hasExecutorRole)
}

// Run the script
main().catch(error => {
console.error(error)
process.exit(1)
})
302 changes: 302 additions & 0 deletions scripts-altlayer/erc20TokenBridgeDeployment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
/* eslint-disable no-empty */
import { BigNumber, ContractFactory, Signer, Wallet, ethers } from 'ethers'
import { JsonRpcProvider } from '@ethersproject/providers'
import {
L1ToL2MessageGasEstimator,
L1ToL2MessageStatus,
L1TransactionReceipt,
} from '@arbitrum/sdk'
import { exit } from 'process'
import { getBaseFee } from '@arbitrum/sdk/dist/lib/utils/lib'
import { RollupAdminLogic__factory } from '@arbitrum/sdk/dist/lib/abi/factories/RollupAdminLogic__factory'

type NamedFactory = ContractFactory & { contractName: string }

const NamedFactoryInstance = (contractJson: {
abi: any
bytecode: string
contractName: string
}): NamedFactory => {
const factory = new ContractFactory(
contractJson.abi,
contractJson.bytecode
) as NamedFactory
factory['contractName'] = contractJson.contractName
return factory
}

// import from token-bridge-contracts directly to make sure the bytecode is the same

import L2AtomicTokenBridgeFactory from '@arbitrum/token-bridge-contracts/build/contracts/contracts/tokenbridge/arbitrum/L2AtomicTokenBridgeFactory.sol/L2AtomicTokenBridgeFactory.json'
const L2AtomicTokenBridgeFactory__factory = NamedFactoryInstance(
L2AtomicTokenBridgeFactory
)
// import from nitro-contracts directly to make sure the bytecode is the same
import IInbox from '@arbitrum/nitro-contracts/build/contracts/src/bridge/IInbox.sol/IInbox.json'
const IInbox__factory = NamedFactoryInstance(IInbox)
import IERC20Bridge from '@arbitrum/nitro-contracts/build/contracts/src/bridge/IERC20Bridge.sol/IERC20Bridge.json'
const IERC20Bridge__factory = NamedFactoryInstance(IERC20Bridge)
import IERC20 from '@arbitrum/nitro-contracts/build/contracts/@openzeppelin/contracts/token/ERC20/IERC20.sol/IERC20.json'
const IERC20__factory = NamedFactoryInstance(IERC20)

/**
* Use already deployed L1TokenBridgeCreator to create and init token bridge contracts.
* Function first gets estimates for 2 retryable tickets - one for deploying L2 factory and
* one for deploying L2 side of token bridge. Then it creates retryables, waits for
* until they're executed, and finally it picks up addresses of new contracts.
*
* @param l1Signer
* @param l2Provider
* @param l1TokenBridgeCreator
* @param rollupAddress
* @returns
*/
export const createTokenBridge = async (
l1Signer: Signer,
l2Provider: ethers.providers.Provider,
l1TokenBridgeCreator: ethers.Contract,
rollupAddress: string
) => {
const gasPrice = await l2Provider.getGasPrice()
//// run retryable estimate for deploying L2 factory
const deployFactoryGasParams = await getEstimateForDeployingFactory(
l1Signer,
l2Provider
)
const maxGasForFactory =
await l1TokenBridgeCreator.gasLimitForL2FactoryDeployment()
const maxSubmissionCostForFactory = deployFactoryGasParams.maxSubmissionCost
//// run retryable estimate for deploying L2 contracts
//// we do this estimate using L2 factory template on L1 because on L2 factory does not yet exist
const l2FactoryTemplate = L2AtomicTokenBridgeFactory__factory.attach(
await l1TokenBridgeCreator.l2TokenBridgeFactoryTemplate()
).connect(l1Signer)
const l2Code = {
router: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2RouterTemplate()
),
standardGateway: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2StandardGatewayTemplate()
),
customGateway: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2CustomGatewayTemplate()
),
wethGateway: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2WethGatewayTemplate()
),
aeWeth: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2WethTemplate()
),
upgradeExecutor: await l1Signer.provider?.getCode(
(
await l1TokenBridgeCreator.l1Templates()
).upgradeExecutor
),
multicall: await l1Signer.provider?.getCode(
await l1TokenBridgeCreator.l2MulticallTemplate()
),
}
const gasEstimateToDeployContracts =
await l2FactoryTemplate.estimateGas.deployL2Contracts(
l2Code,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address,
ethers.Wallet.createRandom().address
)
const maxGasForContracts = gasEstimateToDeployContracts.mul(2)
const maxSubmissionCostForContracts =
deployFactoryGasParams.maxSubmissionCost.mul(2)
let retryableFee = maxSubmissionCostForFactory
.add(maxSubmissionCostForContracts)
.add(maxGasForFactory.mul(gasPrice))
.add(maxGasForContracts.mul(gasPrice))

// get inbox from rollup contract
const inbox = await RollupAdminLogic__factory.connect(
rollupAddress,
l1Signer.provider!
).inbox()
// if fee token is used approve the fee
const feeToken = await _getFeeToken(inbox, l1Signer.provider!)
if (feeToken != ethers.constants.AddressZero) {
await (
await IERC20__factory.attach(feeToken)
.connect(l1Signer)
.attach(feeToken)
.approve(l1TokenBridgeCreator.address, retryableFee)
).wait()
retryableFee = BigNumber.from(0)
}
/// do it - create token bridge
const receipt = await (
await l1TokenBridgeCreator.createTokenBridge(
inbox,
await l1Signer.getAddress(),
maxGasForContracts,
gasPrice,
{ value: retryableFee }
)
).wait()
console.log('Deployment TX:', receipt.transactionHash)

/// wait for execution of both tickets
const l1TxReceipt = new L1TransactionReceipt(receipt)
const messages = await l1TxReceipt.getL1ToL2Messages(l2Provider)
const messageResults = await Promise.all(
messages.map(message => message.waitForStatus())
)

// if both tickets are not redeemed log it and exit
if (
messageResults[0].status !== L1ToL2MessageStatus.REDEEMED ||
messageResults[1].status !== L1ToL2MessageStatus.REDEEMED
) {
console.log(
`Retryable ticket (ID ${messages[0].retryableCreationId}) status: ${
L1ToL2MessageStatus[messageResults[0].status]
}`
)
console.log(
`Retryable ticket (ID ${messages[1].retryableCreationId}) status: ${
L1ToL2MessageStatus[messageResults[1].status]
}`
)
exit()
}

/// pick up L2 factory address from 1st ticket
const l2AtomicTokenBridgeFactory = L2AtomicTokenBridgeFactory__factory.attach(
messageResults[0].l2TxReceipt.contractAddress
).connect(l2Provider)
console.log('L2AtomicTokenBridgeFactory', l2AtomicTokenBridgeFactory.address)

const res = getParsedLogs(
receipt.logs,
l1TokenBridgeCreator.interface,
'OrbitTokenBridgeCreated'
)[0].args

/// pick up L1 contracts from events
const {
l2Deployment: l2Deployment,
l1Deployment: l1Deployment,
proxyAdmin: l1ProxyAdmin,
} = res

/// pick up L2 contracts
const l2Router = l2Deployment.router

const l2StandardGateway = l2Deployment.standardGateway

const beaconProxyFactory = l2Deployment.beaconProxyFactory
const l2CustomGateway = l2Deployment.customGateway

const isUsingFeeToken = feeToken != ethers.constants.AddressZero
const l2WethGateway = isUsingFeeToken
? ethers.constants.AddressZero
: l2Deployment.wethGateway

const l1Weth = l1Deployment.weth
const l2Weth = isUsingFeeToken
? ethers.constants.AddressZero
: l2Deployment.weth
const l2ProxyAdmin = l2Deployment.proxyAdmin

const l1MultiCall = await l1TokenBridgeCreator.l1Multicall()
const l2Multicall = l2Deployment.multicall

const l1Router = l1Deployment.router
const l1StandardGateway = l1Deployment.standardGateway
const l1CustomGateway = l1Deployment.customGateway
const l1WethGateway = l1Deployment.wethGateway

return {
l1Router,
l1StandardGateway,
l1CustomGateway,
l1WethGateway,
l1ProxyAdmin,
l2Router,
l2StandardGateway,
l2CustomGateway,
l2WethGateway,
l1Weth,
l2Weth,
beaconProxyFactory,
l2ProxyAdmin,
l1MultiCall,
l2Multicall,
}
}

export const getEstimateForDeployingFactory = async (
l1Deployer: Signer,
l2Provider: ethers.providers.Provider
) => {
//// run retryable estimate for deploying L2 factory
const l1DeployerAddress = await l1Deployer.getAddress()
const l1ToL2MsgGasEstimate = new L1ToL2MessageGasEstimator(l2Provider)
const deployFactoryGasParams = await l1ToL2MsgGasEstimate.estimateAll(
{
from: ethers.Wallet.createRandom().address,
to: ethers.constants.AddressZero,
l2CallValue: BigNumber.from(0),
excessFeeRefundAddress: l1DeployerAddress,
callValueRefundAddress: l1DeployerAddress,
data: L2AtomicTokenBridgeFactory__factory.bytecode,
},
await getBaseFee(l1Deployer.provider!),
l1Deployer.provider!
)

return deployFactoryGasParams
}

export const getSigner = (provider: JsonRpcProvider, key?: string) => {
if (!key && !provider)
throw new Error('Provide at least one of key or provider.')
if (key) return new Wallet(key).connect(provider)
else return provider.getSigner(0)
}

export const getParsedLogs = (
logs: ethers.providers.Log[],
iface: ethers.utils.Interface,
eventName: string
) => {
const eventFragment = iface.getEvent(eventName)
const parsedLogs = logs
.filter(
(curr: any) => curr.topics[0] === iface.getEventTopic(eventFragment)
)
.map((curr: any) => iface.parseLog(curr))
return parsedLogs
}

const _getFeeToken = async (
inbox: string,
l1Provider: ethers.providers.Provider
) => {
const bridge = await IInbox__factory.attach(inbox)
.connect(l1Provider)
.bridge()

let feeToken = ethers.constants.AddressZero

try {
feeToken = await IERC20Bridge__factory.attach(bridge)
.connect(l1Provider)
.nativeToken()
} catch {}

return feeToken
}

export function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
Loading

0 comments on commit dfb9b71

Please sign in to comment.