diff --git a/package.json b/package.json index 08aac72..0691e0d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "orbit-setup-script", "dependencies": { "@arbitrum/nitro-contracts": "^1.1.1", - "@arbitrum/orbit-sdk": "^0.8.0", + "@arbitrum/orbit-sdk": "^0.9.0", "@arbitrum/token-bridge-contracts": "^1.2.1", "viem": "^1.20.0" }, diff --git a/scripts/createTokenBridge.ts b/scripts/createTokenBridge.ts index 98373ad..a057c20 100644 --- a/scripts/createTokenBridge.ts +++ b/scripts/createTokenBridge.ts @@ -91,30 +91,32 @@ async function getNativeToken({ * @returns */ export const createNewTokenBridge = async ( - baseChainRpc: string, - baseChainDeployerKey: string, - childChainRpc: string, + parentChainRpc: string, + parentChainDeployerKey: string, + orbitChainRpc: string, rollupAddress: string ) => { - const l1Provider = new JsonRpcProvider(baseChainRpc) + const l1Provider = new JsonRpcProvider(parentChainRpc) const l1NetworkInfo = await l1Provider.getNetwork() - const l2Provider = new JsonRpcProvider(childChainRpc) + const l2Provider = new JsonRpcProvider(orbitChainRpc) const l2NetworkInfo = await l2Provider.getNetwork() - const deployer = privateKeyToAccount(sanitizePrivateKey(baseChainDeployerKey)) + const deployer = privateKeyToAccount( + sanitizePrivateKey(parentChainDeployerKey) + ) const rollup = RollupAdminLogic__factory.connect(rollupAddress, l1Provider) const parentChainPublicClient = createPublicClientFromChainInfo({ id: l1NetworkInfo.chainId, name: l1NetworkInfo.name, - rpcUrl: baseChainRpc, + rpcUrl: parentChainRpc, }) const orbitChainPublicClient = createPublicClientFromChainInfo({ id: l2NetworkInfo.chainId, name: l2NetworkInfo.name, - rpcUrl: childChainRpc, + rpcUrl: orbitChainRpc, }) const nativeToken = await getNativeToken({ @@ -323,17 +325,17 @@ export const createNewTokenBridge = async ( } export const createERC20Bridge = async ( - baseChainRpc: string, - baseChainDeployerKey: string, - childChainRpc: string, + parentChainRpc: string, + parentChainDeployerKey: string, + orbitChainRpc: string, rollupAddress: string ) => { console.log('Creating token bridge for rollup', rollupAddress) const { l1Network, l2Network } = await createNewTokenBridge( - baseChainRpc, - baseChainDeployerKey, - childChainRpc, + parentChainRpc, + parentChainDeployerKey, + orbitChainRpc, rollupAddress ) const NETWORK_FILE = 'network.json' diff --git a/scripts/l3Configuration.ts b/scripts/l3Configuration.ts index 260753c..b0700ec 100644 --- a/scripts/l3Configuration.ts +++ b/scripts/l3Configuration.ts @@ -1,25 +1,67 @@ -import { abi as ArbOwner__abi } from '@arbitrum/nitro-contracts/build/contracts/src/precompiles/ArbOwner.sol/ArbOwner.json' -import { abi as ArbGasInfo__abi } from '@arbitrum/nitro-contracts/build/contracts/src/precompiles/ArbGasInfo.sol/ArbGasInfo.json' -import { ethers } from 'ethers' import { L3Config } from './l3ConfigType' import fs from 'fs' +import { JsonRpcProvider } from '@ethersproject/providers' + +import { createPublicClient, defineChain, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' + +import { arbOwnerPublicActions } from '@arbitrum/orbit-sdk' +import { arbGasInfoPublicActions } from '@arbitrum/orbit-sdk' + +import { sanitizePrivateKey } from '@arbitrum/orbit-sdk/utils' + +function createPublicClientFromChainInfo({ + id, + name, + rpcUrl, +}: { + id: number + name: string + rpcUrl: string +}) { + const chain = defineChain({ + id: id, + network: name, + name: name, + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { + http: [rpcUrl], + }, + public: { + http: [rpcUrl], + }, + }, + testnet: true, + }) + return createPublicClient({ chain, transport: http() }) +} export async function l3Configuration( - privateKey: string, - L2_RPC_URL: string, - L3_RPC_URL: string + parentChainDeployerKey: string, + orbitChainRpc: string ) { - if (!privateKey || !L2_RPC_URL || !L3_RPC_URL) { + if (!parentChainDeployerKey || !orbitChainRpc) { 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) + // Generation parent chain provider and network + const l2Provider = new JsonRpcProvider(orbitChainRpc) + const l2NetworkInfo = await l2Provider.getNetwork() + + //Generating deployer signer + const deployer = privateKeyToAccount( + sanitizePrivateKey(parentChainDeployerKey) + ) - // Creating the wallet and signer - const l2signer = new ethers.Wallet(privateKey).connect(L2Provider) - const l3signer = new ethers.Wallet(privateKey).connect(L3Provider) + // Creating Orbit chain client with arb owner and arb gas info extension + const orbitChainPublicClient = createPublicClientFromChainInfo({ + id: l2NetworkInfo.chainId, + name: l2NetworkInfo.name, + rpcUrl: orbitChainRpc, + }) + .extend(arbOwnerPublicActions) + .extend(arbGasInfoPublicActions) // Read the JSON configuration const configRaw = fs.readFileSync( @@ -28,108 +70,153 @@ export async function l3Configuration( ) const config: L3Config = JSON.parse(configRaw) - // Reading params for L3 Configuration - const minL2BaseFee = config.minL2BaseFee - const networkFeeReceiver = config.networkFeeReceiver - const infrastructureFeeCollector = config.infrastructureFeeCollector + // Reading params for Configuration + const minOrbitChainBaseFee = config.minL2BaseFee + const networkFeeReceiver = config.networkFeeReceiver as `0x${string}` + const infrastructureFeeCollector = + config.infrastructureFeeCollector as `0x${string}` const chainOwner = config.chainOwner // Check if the Private Key provided is the chain owner: - if (l3signer.address !== chainOwner) { + if (deployer.address !== chainOwner) { throw new Error('The Private Key you have provided is not the chain owner') } - // ArbOwner precompile setup - const arbOwnerABI = ArbOwner__abi - - // Arb Owner precompile address - const arbOwnerAddress = '0x0000000000000000000000000000000000000070' - const ArbOwner = new ethers.Contract(arbOwnerAddress, arbOwnerABI, l3signer) - // Call the isChainOwner function and check the response - const isSignerChainOwner = await ArbOwner.isChainOwner(l3signer.address) - if (!isSignerChainOwner) { + const isOwnerInitially = await orbitChainPublicClient.arbOwnerReadContract({ + functionName: 'isChainOwner', + args: [deployer.address], + }) + + // assert account is not already an owner + if (!isOwnerInitially) { throw new Error('The address you have provided is not the chain owner') } - // Set the network base fee + // Set the orbit chain base fee console.log('Setting the Minimum Base Fee for the Orbit chain') - const tx = await ArbOwner.setMinimumL2BaseFee(minL2BaseFee) - // Wait for the transaction to be mined - const receipt = await tx.wait() - console.log( - `Minimum Base Fee is set on the block number ${await receipt.blockNumber} on the Orbit chain` - ) - - // Check the status of the transaction: 1 is successful, 0 is failure - if (receipt.status === 0) { - throw new Error('Transaction failed, could not set the Minimum base fee') + const setMinimumBaseFeeTransactionRequest = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'setMinimumL2BaseFee', + args: [BigInt(minOrbitChainBaseFee)], + upgradeExecutor: false, + account: deployer.address, + }) + // submit tx to update minimum child chain base fee + const setMinimumBaseFeeTransactionHash = + await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction( + setMinimumBaseFeeTransactionRequest + ), + }) + await orbitChainPublicClient.waitForTransactionReceipt({ + hash: setMinimumBaseFeeTransactionHash, + }) + // Get the updated minimum basefee on orbit chain from arbGasInfo precompile on child chain + const minOrbitChainBaseFeeRead = + await orbitChainPublicClient.arbGasInfoReadContract({ + functionName: 'getMinimumGasPrice', + }) + // Check if minimum basefee param is set correctly on orbit chain + if (Number(minOrbitChainBaseFeeRead) === minOrbitChainBaseFee) { + console.log('Minimum L3 base fee is set') + } else { + throw new Error('Failed to set Minimum L3 base fee') } - // Set the network fee receiver - console.log('Setting the network fee receiver for the Orbit chain') - const tx2 = await ArbOwner.setNetworkFeeAccount(networkFeeReceiver) - - // Wait for the transaction to be mined - const receipt2 = await tx2.wait() - console.log( - `network fee receiver is set on the block number ${await receipt2.blockNumber} on the Orbit chain` - ) - - // Check the status of the transaction: 1 is successful, 0 is failure - if (receipt2.status === 0) { + // Set the network fee account + const setNetworkFeeAccountTransactionRequest = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'setNetworkFeeAccount', + args: [networkFeeReceiver], + upgradeExecutor: false, + account: deployer.address, + }) + + // submit tx to update infra fee receiver + const setNetworkFeeAccountTransactionHash = + await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction( + setNetworkFeeAccountTransactionRequest + ), + }) + await orbitChainPublicClient.waitForTransactionReceipt({ + hash: setNetworkFeeAccountTransactionHash, + }) + + // check if network fee account is updated correctly + const networkFeeRecieverAccount = + await orbitChainPublicClient.arbOwnerReadContract({ + functionName: 'getNetworkFeeAccount', + }) + + if (networkFeeRecieverAccount === networkFeeReceiver) { + console.log('network fee receiver is set') + } else { throw new Error( 'network fee receiver Setting network fee receiver transaction failed' ) } - // Set the infrastructure fee collector - console.log( - 'Setting the infrastructure fee collector address for the Orbit chain' - ) - const tx3 = await ArbOwner.setInfraFeeAccount(infrastructureFeeCollector) - - // Wait for the transaction to be mined - const receipt3 = await tx3.wait() - console.log( - `infrastructure fee collector address is set on the block number ${await receipt3.blockNumber} on the Orbit chain` - ) - - // Check the status of the transaction: 1 is successful, 0 is failure - if (receipt3.status === 0) { - throw new Error( - 'Setting Set the infrastructure fee collector transaction failed' - ) + // Set the infra fee account + const setInfraFeeAccountTransactionRequest = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'setInfraFeeAccount', + args: [infrastructureFeeCollector], + upgradeExecutor: false, + account: deployer.address, + }) + + // submit tx to update infra fee receiver + const setInfraFeeAccountTransactionHash = + await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction( + setInfraFeeAccountTransactionRequest + ), + }) + await orbitChainPublicClient.waitForTransactionReceipt({ + hash: setInfraFeeAccountTransactionHash, + }) + const infraFeeReceiver = await orbitChainPublicClient.arbOwnerReadContract({ + functionName: 'getInfraFeeAccount', + }) + + // check if infra fee account is updated correctly + if (infraFeeReceiver === infrastructureFeeCollector) { + console.log('Infra Fee Collector changed successfully') + } else { + throw new Error('Infra Fee Collector Setting transaction failed') } - // Setting L1 basefee on L3 - const arbGasInfoAbi = ArbGasInfo__abi - const arbGasInfoAddress = '0x000000000000000000000000000000000000006c' - const ArbOGasInfo = new ethers.Contract( - arbGasInfoAddress, - arbGasInfoAbi, - l2signer - ) - + // Setting L1 basefee estimate on L3 console.log('Getting L1 base fee estimate') - const l1BaseFeeEstimate = await ArbOGasInfo.getL1BaseFeeEstimate() - console.log(`L1 Base Fee estimate on L2 is ${l1BaseFeeEstimate.toNumber()}`) - const l2Basefee = await L2Provider.getGasPrice() - const totalGasPrice = await l1BaseFeeEstimate.add(l2Basefee) - console.log(`Setting L1 base fee estimate on L3 to ${totalGasPrice}`) - const tx4 = await ArbOwner.setL1PricePerUnit(totalGasPrice) - - // Wait for the transaction to be mined - const receipt4 = await tx4.wait() - console.log( - `L1 base fee estimate is set on the block number ${await receipt4.blockNumber} on the Orbit chain` + const l1BaseFeeEstimate = await orbitChainPublicClient.arbGasInfoReadContract( + { + functionName: 'getL1BaseFeeEstimate', + } ) + console.log(`L1 Base Fee estimate on L2 is ${Number(l1BaseFeeEstimate)}`) + const l2Basefee = await l2Provider.getGasPrice() + const totalGasPrice = l2Basefee.add(l1BaseFeeEstimate) + console.log(`Setting L1 base fee estimate on L3 to ${totalGasPrice}`) - // Check the status of the transaction: 1 is successful, 0 is failure - if (receipt4.status === 0) { - throw new Error('Base Fee Setting failed') - } - - console.log('All things done! Enjoy your Orbit chain. LFG 🚀🚀🚀🚀') + const setL1PricePerUnitTransactionRequest = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'setL1PricePerUnit', + args: [BigInt(totalGasPrice.toString())], + upgradeExecutor: false, + account: deployer.address, + }) + + // setting setL1PricePerUnit + const setL1PricePerUnitTransactionHash = + await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction( + setL1PricePerUnitTransactionRequest + ), + }) + await orbitChainPublicClient.waitForTransactionReceipt({ + hash: setL1PricePerUnitTransactionHash, + }) } diff --git a/scripts/setup.ts b/scripts/setup.ts index 5f325a5..17d8242 100644 --- a/scripts/setup.ts +++ b/scripts/setup.ts @@ -167,7 +167,7 @@ async function main() { console.log( 'Running l3Configuration script to configure your Orbit chain 📝📝📝📝📝' ) - await l3Configuration(privateKey, L2_RPC_URL, L3_RPC_URL) + await l3Configuration(privateKey, L3_RPC_URL) rs.l3config = true } //////////////////////////////// @@ -177,8 +177,9 @@ async function main() { console.log( 'Transferring ownership on L3, from rollup owner to upgrade executor 🔃🔃🔃' ) - await transferOwner(privateKey, L2Provider, L3Provider) + await transferOwner(privateKey, L2Provider, L3Provider, L3_RPC_URL) rs.transferOwnership = true + console.log('All things done! Enjoy your Orbit chain. LFG 🚀🚀🚀🚀') } } catch (error) { console.error('Error occurred:', error) @@ -189,7 +190,6 @@ async function main() { ) } } - // Run the script main().catch(error => { console.error(error) diff --git a/scripts/transferOwnership.ts b/scripts/transferOwnership.ts index 3b5d6f6..6a65e94 100644 --- a/scripts/transferOwnership.ts +++ b/scripts/transferOwnership.ts @@ -1,12 +1,41 @@ import { ethers, Wallet } from 'ethers' import { JsonRpcProvider } from '@ethersproject/providers' -import UpgradeExecutor from '@arbitrum/nitro-contracts/build/contracts/src/mocks/UpgradeExecutorMock.sol/UpgradeExecutorMock.json' -import ArbOwner from '@arbitrum/nitro-contracts/build/contracts/src/precompiles/ArbOwner.sol/ArbOwner.json' import fs from 'fs' import { L3Config } from './l3ConfigType' import { TOKEN_BRIDGE_CREATOR_Arb_Sepolia } from './createTokenBridge' import L1AtomicTokenBridgeCreator from '@arbitrum/token-bridge-contracts/build/contracts/contracts/tokenbridge/ethereum/L1AtomicTokenBridgeCreator.sol/L1AtomicTokenBridgeCreator.json' +import { arbOwnerPublicActions } from '@arbitrum/orbit-sdk' +import { createPublicClient, defineChain, http } from 'viem' +import { privateKeyToAccount } from 'viem/accounts' +import { sanitizePrivateKey } from '@arbitrum/orbit-sdk/utils' + +function createPublicClientFromChainInfo({ + id, + name, + rpcUrl, +}: { + id: number + name: string + rpcUrl: string +}) { + const chain = defineChain({ + id: id, + network: name, + name: name, + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + rpcUrls: { + default: { + http: [rpcUrl], + }, + public: { + http: [rpcUrl], + }, + }, + testnet: true, + }) + return createPublicClient({ chain, transport: http() }) +} export const getSigner = (provider: JsonRpcProvider, key?: string) => { if (!key && !provider) @@ -15,14 +44,15 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { else return provider.getSigner(0) } -const ARB_OWNER_ADDRESS = '0x0000000000000000000000000000000000000070' export async function transferOwner( privateKey: string, l2Provider: ethers.providers.JsonRpcProvider, - l3Provider: ethers.providers.JsonRpcProvider + l3Provider: ethers.providers.JsonRpcProvider, + orbitChainRpc: string ) { - //Generating l2 and l3 deployer signers from privatekey and providers - const l3Deployer = getSigner(l3Provider, privateKey) + //Generating deployer signer + const deployer = privateKeyToAccount(sanitizePrivateKey(privateKey)) + //fetching chain id of parent chain const l2ChainId = (await l2Provider.getNetwork()).chainId @@ -34,7 +64,13 @@ export async function transferOwner( 'The Base Chain you have provided is not supported, please put RPC for Arb Sepolia' ) } - + const l3NetworkInfo = await l3Provider.getNetwork() + // Creating Orbit chain client + const orbitChainPublicClient = createPublicClientFromChainInfo({ + id: l3NetworkInfo.chainId, + name: l3NetworkInfo.name, + rpcUrl: orbitChainRpc, + }).extend(arbOwnerPublicActions) // Read the JSON configuration const configRaw = fs.readFileSync( './config/orbitSetupScriptConfig.json', @@ -55,41 +91,53 @@ export async function transferOwner( await l1TokenBridgeCreator.inboxToL2Deployment(config.inbox) ).upgradeExecutor - //Defining Arb Owner Precompile - const ArbOwner__factory = new ethers.Contract( - ARB_OWNER_ADDRESS, - ArbOwner.abi, - l3Deployer - ) - const ArbOwnerContract = ArbOwner__factory.connect(l3Deployer) console.log('Adding Upgrade Executor contract to the chain owners') - const receipt1 = await ( - await ArbOwnerContract.addChainOwner(executorContractAddress) - ).wait() + const transactionRequest1 = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'addChainOwner', + args: [executorContractAddress], + upgradeExecutor: false, + account: deployer.address, + }) + // submit tx to add chain owner + const txHash1 = await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction(transactionRequest1), + }) + const txReceipt1 = await orbitChainPublicClient.waitForTransactionReceipt({ + hash: txHash1, + }) console.log( - 'Executor has been added to chain owners on TX:', - receipt1.transactionHash - ) - //Defining upgrade executor contract - const executorContract__factory = new ethers.Contract( - executorContractAddress, - UpgradeExecutor.abi, - l3Deployer - ) - const upgradeExecutor = executorContract__factory.connect(l3Deployer) - //Constructing call data for removing rollup owner from chain owners on L3 - const arbOwnerInterface = new ethers.utils.Interface(ArbOwner.abi) - const targetCallData = arbOwnerInterface.encodeFunctionData( - 'removeChainOwner', - [await l3Deployer.getAddress()] + `UpgradeExecutor account has been added to chain owners in ${txReceipt1.transactionHash}` ) - console.log('Executing removeChainOwner through the UpgradeExecutor contract') - const receipt2 = await ( - await upgradeExecutor.executeCall(ARB_OWNER_ADDRESS, targetCallData) - ).wait() + // Removing deployer as chain owner + const transactionRequest2 = + await orbitChainPublicClient.arbOwnerPrepareTransactionRequest({ + functionName: 'removeChainOwner', + args: [deployer.address], + upgradeExecutor: executorContractAddress, + account: deployer.address, + }) + + // submit tx to remove chain owner + const txHash2 = await orbitChainPublicClient.sendRawTransaction({ + serializedTransaction: await deployer.signTransaction(transactionRequest2), + }) + const txReceipt2 = await orbitChainPublicClient.waitForTransactionReceipt({ + hash: txHash2, + }) console.log( - 'Transaction complete, rollup owner removed from chain owners on TX:', - receipt2.transactionHash + `Deployer account removed from chain owners in ${txReceipt2.transactionHash}` ) + + // Checking chain onwers to see if deployer account is removed + const isOwner2 = await orbitChainPublicClient.arbOwnerReadContract({ + functionName: 'isChainOwner', + args: [deployer.address], + }) + if (!isOwner2) { + console.log( + 'UpgradeExecutor contract has been added to chain owners successfully' + ) + } } diff --git a/yarn.lock b/yarn.lock index 00a2955..2b89856 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,12 +22,12 @@ "@openzeppelin/contracts-upgradeable" "4.5.2" patch-package "^6.4.7" -"@arbitrum/orbit-sdk@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@arbitrum/orbit-sdk/-/orbit-sdk-0.8.0.tgz#67b437afb2b2d17f9ec28391bcbbde9689c42a04" - integrity sha512-UrcCOqDA3crCKIhOW957DmGvglPkpU/VT1QvcgCD+i4UFjkyT0/aMizVoj8AgsLaq5X46trMN2HKBkrAEjhLlg== +"@arbitrum/orbit-sdk@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@arbitrum/orbit-sdk/-/orbit-sdk-0.9.0.tgz#681e2c9fe05cf36de326e290481b466ec2f1a2c9" + integrity sha512-alwpZeSaQH/i9j2t6OZ6TO4wrzJM7CbSIwklvkI3vkqnto/1eiS3W0MpkStmZEAwe8EIMJKlkAsuPDIctv2uNQ== dependencies: - "@arbitrum/sdk" "^3.2.0" + "@arbitrum/sdk" "^3.3.2" "@arbitrum/token-bridge-contracts" "^1.2.1" ethers "^5.7.2" @@ -42,6 +42,17 @@ async-mutex "^0.4.0" ethers "^5.1.0" +"@arbitrum/sdk@^3.3.2": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.3.3.tgz#9a49e753b9deeee6336f6a43acb0669961b91128" + integrity sha512-Fe/+h2gs78Efl94U7yXkYAN+FQLLAxDGj+kv6+CXbnUytso+PBvKgNj1e4YPwgtWLlDomdcOXFt/yaP4VKsrBQ== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + async-mutex "^0.4.0" + ethers "^5.1.0" + "@arbitrum/token-bridge-contracts@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.2.1.tgz#4838d70182bc0d6b36adfd733d7b4650e596c979"