diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index f3b2d5bb6..0ee667576 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -161,26 +161,27 @@ jobs: name: Test (Integration) - ${{ matrix.config.name }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: config: - name: Nitro contracts v3.2 - Custom gas token with 18 decimals - nitro-contracts-branch: v3.2.0 - args: --tokenbridge --l3node --l3-token-bridge --l3-fee-token + version: v0.2.7 + nitro-contracts-version: v3.2 decimals: 18 - name: Nitro contracts v3.2 - Custom gas token with 6 decimals - nitro-contracts-branch: v3.2.0 - args: --tokenbridge --l3node --l3-token-bridge --l3-fee-token --l3-fee-token-decimals 6 + version: v0.2.7 + nitro-contracts-version: v3.2 decimals: 6 - name: Nitro contracts v2.1 - Custom gas token with 18 decimals - nitro-contracts-branch: v2.1.3 - args: --tokenbridge --l3node --l3-token-bridge --l3-fee-token + version: v0.2.7 + nitro-contracts-version: v2.1 decimals: 18 - name: Nitro contracts v2.1 - Custom gas token with 6 decimals - nitro-contracts-branch: v2.1.3 - args: --tokenbridge --l3node --l3-token-bridge --l3-fee-token --l3-fee-token-decimals 6 + version: v0.2.7 + nitro-contracts-version: v2.1 decimals: 6 steps: @@ -205,11 +206,13 @@ jobs: cache-key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} - name: Set up the local node - uses: OffchainLabs/actions/run-nitro-test-node@feat-simplify + uses: OffchainLabs/arbitrum-testnode@v0.2.7 with: - nitro-testnode-ref: release - nitro-contracts-branch: ${{ matrix.config.nitro-contracts-branch }} - args: ${{ matrix.config.args }} + version: ${{ matrix.config.version }} + l3-enabled: true + fee-token-decimals: ${{ matrix.config.decimals }} + nitro-contracts-version: ${{ matrix.config.nitro-contracts-version }} + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Copy .env run: cp ./.env.example ./.env @@ -219,8 +222,8 @@ jobs: - name: Test run: | - INTEGRATION_TEST_DECIMALS=${{matrix.config.decimals}} \ - INTEGRATION_TEST_NITRO_CONTRACTS_BRANCH=${{matrix.config.nitro-contracts-branch}} \ + INTEGRATION_TEST_DECIMALS=${{ matrix.config.decimals }} \ + INTEGRATION_TEST_NITRO_CONTRACTS_BRANCH=${{ matrix.config.nitro-contracts-version }} \ pnpm test:integration test-integration-arbitrum-testnode: diff --git a/audit-ci.jsonc b/audit-ci.jsonc index ed9515626..9f7d4f380 100644 --- a/audit-ci.jsonc +++ b/audit-ci.jsonc @@ -70,7 +70,44 @@ // elliptic has no patched release yet (affected: <=6.6.1) // transitive via ethereumjs-util -> ethereum-cryptography -> secp256k1 -> elliptic // from: @safe-global/protocol-kit>ethereumjs-util>ethereum-cryptography>secp256k1>elliptic - "GHSA-848j-6mx2-7j84" + "GHSA-848j-6mx2-7j84", + + // flatted + //////////// + // https://github.com/advisories/GHSA-25h7-pfq9-p65f + // flatted ReDoS - transitive dev dep via eslint>file-entry-cache>flat-cache>flatted + // not a runtime dependency + "GHSA-25h7-pfq9-p65f", + // https://github.com/advisories/GHSA-rf6f-7fwh-wjgh + // flatted DoS - transitive dev dep via eslint>file-entry-cache>flat-cache>flatted + // not a runtime dependency + "GHSA-rf6f-7fwh-wjgh", + + // picomatch + //////////// + // https://github.com/advisories/GHSA-3v7f-55p6-f55p + // picomatch method injection - transitive dev dep via @wagmi/cli>chokidar and typescript-eslint + // not a runtime dependency + "GHSA-3v7f-55p6-f55p", + // https://github.com/advisories/GHSA-c2c7-rcm5-vvqj + // picomatch ReDoS - transitive dev dep via @wagmi/cli>chokidar and typescript-eslint + // not a runtime dependency + "GHSA-c2c7-rcm5-vvqj", + + // brace-expansion + //////////// + // https://github.com/advisories/GHSA-f886-m6hf-6m8v + // brace-expansion infinite loop - transitive dev dep via eslint>minimatch, ts-morph, typescript-eslint + // not a runtime dependency + "GHSA-f886-m6hf-6m8v", + + // yaml + //////////// + // https://github.com/advisories/GHSA-48c2-rrv3-qjmp + // yaml stack overflow via deeply nested collections + // transitive dev dep via patch-package>yaml and @arbitrum/nitro-contracts>patch-package>yaml + // not a runtime dependency + "GHSA-48c2-rrv3-qjmp" ] } diff --git a/src/actions/sequencerInbox.integration.test.ts b/src/actions/sequencerInbox.integration.test.ts index 7f11a2690..d996d5bec 100644 --- a/src/actions/sequencerInbox.integration.test.ts +++ b/src/actions/sequencerInbox.integration.test.ts @@ -3,9 +3,9 @@ import { createRollupHelper, getInformationFromTestnode, getNitroTestnodePrivateKeyAccounts, + testHelper_getNitroTestnodeL2, } from '../testHelpers'; import { Hex, createPublicClient, http, zeroAddress } from 'viem'; -import { nitroTestnodeL2 } from '../chains'; import { getMaxTimeVariation } from './getMaxTimeVariation'; import { isBatchPoster } from './isBatchPoster'; import { sequencerInboxABI } from '../contracts/SequencerInbox'; @@ -20,7 +20,7 @@ const { l3SequencerInbox, l3BatchPoster, l3UpgradeExecutor } = getInformationFro const { l3TokenBridgeDeployer, deployer, l3RollupOwner } = getNitroTestnodePrivateKeyAccounts(); const client = createPublicClient({ - chain: nitroTestnodeL2, + chain: testHelper_getNitroTestnodeL2(), transport: http(), }); diff --git a/src/contracts/RollupCreator/v2.1.ts b/src/contracts/RollupCreator/v2.1.ts index fb93b7317..61879d813 100644 --- a/src/contracts/RollupCreator/v2.1.ts +++ b/src/contracts/RollupCreator/v2.1.ts @@ -218,7 +218,7 @@ export const rollupCreatorAddress = { 42161: '0x79607f00e61E6d7C0E6330bd7E9c4AC320D50FC9', 42170: '0x9B523BF5F77e8d90e0E9eb0924aEA6E40B081aE6', 84532: '0x6e244cD02BBB8a6dbd7F626f05B2ef82151Ab502', - 412346: '0x4287839696d650A0cf93b98351e85199102335D0', + 412346: '0x1E08B9c3f94E9aBcc531f67F949d796eC76963b9', 421614: '0xd2Ec8376B1dF436fAb18120E416d3F2BeC61275b', 11155111: '0xfb774eA8A92ae528A596c8D90CBCF1bdBC4Cee79', } as const; diff --git a/src/contracts/RollupCreator/v3.2.ts b/src/contracts/RollupCreator/v3.2.ts index 0fa4e849e..cc7ae3cd2 100644 --- a/src/contracts/RollupCreator/v3.2.ts +++ b/src/contracts/RollupCreator/v3.2.ts @@ -266,7 +266,7 @@ export const rollupCreatorAddress = { 42161: '0xF5962AD061A1aD6F38F340F5b267b3593cC1Cd7B', 42170: '0xF5962AD061A1aD6F38F340F5b267b3593cC1Cd7B', 84532: '0x8d1668636D053C10F57367D68118bD624f41ffe6', - 412346: '0xbcF51F3AAb5D5Efa025b4A2B235BDc9F3f69b4d2', + 412346: '0x1E08B9c3f94E9aBcc531f67F949d796eC76963b9', 421614: '0xF5962AD061A1aD6F38F340F5b267b3593cC1Cd7B', 11155111: '0xe06Bc77336E201c4C08751918A4bB99ddf0e1Bf7', } as const; diff --git a/src/createRollup.integration.test.ts b/src/createRollup.integration.test.ts index 98114fafa..7fa414451 100644 --- a/src/createRollup.integration.test.ts +++ b/src/createRollup.integration.test.ts @@ -1,16 +1,16 @@ import { describe, it, expect } from 'vitest'; import { createPublicClient, http, parseGwei, zeroAddress } from 'viem'; -import { nitroTestnodeL2 } from './chains'; import { createRollupHelper, getNitroTestnodePrivateKeyAccounts, getInformationFromTestnode, + testHelper_getNitroTestnodeL2, } from './testHelpers'; import { createRollupFetchTransactionHash } from './createRollupFetchTransactionHash'; const parentChainPublicClient = createPublicClient({ - chain: nitroTestnodeL2, + chain: testHelper_getNitroTestnodeL2(), transport: http(), }); diff --git a/src/createRollup.ts b/src/createRollup.ts index c43a2955d..cc52e7c9c 100644 --- a/src/createRollup.ts +++ b/src/createRollup.ts @@ -21,7 +21,6 @@ type EnsureCustomGasTokenAllowanceGrantedToRollupCreatorParams; account: PrivateKeyAccount; rollupCreatorVersion?: RollupCreatorSupportedVersion; - rollupCreatorAddressOverride?: Address; }; /** @@ -39,14 +38,12 @@ async function ensureCustomGasTokenAllowanceGrantedToRollupCreator< parentChainPublicClient, account, rollupCreatorVersion = 'v3.2', - rollupCreatorAddressOverride, }: EnsureCustomGasTokenAllowanceGrantedToRollupCreatorParams) { const allowanceParams = { nativeToken, account: account.address, publicClient: parentChainPublicClient, rollupCreatorVersion, - rollupCreatorAddressOverride, }; if (!(await createRollupEnoughCustomFeeTokenAllowance(allowanceParams))) { @@ -85,21 +82,18 @@ export type CreateRollupFunctionParams = account: PrivateKeyAccount; parentChainPublicClient: PublicClient; rollupCreatorVersion: 'v2.1'; - rollupCreatorAddressOverride?: Address; } | { params: CreateRollupParams<'v3.2'>; account: PrivateKeyAccount; parentChainPublicClient: PublicClient; rollupCreatorVersion: 'v3.2'; - rollupCreatorAddressOverride?: Address; } | { params: CreateRollupParams<'v3.2'>; account: PrivateKeyAccount; parentChainPublicClient: PublicClient; rollupCreatorVersion?: never; - rollupCreatorAddressOverride?: Address; }; /** @@ -179,7 +173,6 @@ export async function createRollup({ account, parentChainPublicClient, rollupCreatorVersion = 'v3.2', - rollupCreatorAddressOverride, }: CreateRollupFunctionParams): Promise { validateParentChain(parentChainPublicClient); @@ -193,7 +186,6 @@ export async function createRollup({ parentChainPublicClient, account, rollupCreatorVersion, - rollupCreatorAddressOverride, }); } @@ -205,14 +197,12 @@ export async function createRollup({ account: account.address, publicClient: parentChainPublicClient, rollupCreatorVersion: 'v2.1', - rollupCreatorAddressOverride, }) : await createRollupPrepareTransactionRequest({ params: params as CreateRollupParams<'v3.2'>, account: account.address, publicClient: parentChainPublicClient, rollupCreatorVersion: 'v3.2', - rollupCreatorAddressOverride, }); // sign and send the transaction diff --git a/src/createRollupEnoughCustomFeeTokenAllowance.ts b/src/createRollupEnoughCustomFeeTokenAllowance.ts index c7521e5d1..99740607f 100644 --- a/src/createRollupEnoughCustomFeeTokenAllowance.ts +++ b/src/createRollupEnoughCustomFeeTokenAllowance.ts @@ -41,6 +41,7 @@ export async function createRollupEnoughCustomFeeTokenAllowance { expect(orbitChainRetryableReceipts[0].status).toEqual('success'); expect(orbitChainRetryableReceipts[1].status).toEqual('success'); - // get contracts + // get contracts (query the freshly-deployed creator that holds the deployment mapping) const tokenBridgeContracts = await txReceipt.getTokenBridgeContracts({ parentChainPublicClient: nitroTestnodeL1Client, tokenBridgeCreatorAddressOverride: tokenBridgeCreator, @@ -230,13 +229,13 @@ describe('createTokenBridge utils function', () => { ], }), value: BigInt(0), - account: l3TokenBridgeDeployer, + account: l3RollupOwner, }); // sign and send the transaction const fundTxRequest = { ...fundTxRequestRaw, chainId: nitroTestnodeL2Client.chain.id }; const fundTxHash = await nitroTestnodeL2Client.sendRawTransaction({ - serializedTransaction: await l3TokenBridgeDeployer.signTransaction(fundTxRequest), + serializedTransaction: await l3RollupOwner.signTransaction(fundTxRequest), }); // get the transaction receipt after waiting for the transaction to complete @@ -327,7 +326,7 @@ describe('createTokenBridge utils function', () => { expect(orbitChainRetryableReceipts[0].status).toEqual('success'); expect(orbitChainRetryableReceipts[1].status).toEqual('success'); - // get contracts + // get contracts (query the freshly-deployed creator that holds the deployment mapping) const tokenBridgeContracts = await txReceipt.getTokenBridgeContracts({ parentChainPublicClient: nitroTestnodeL2Client, tokenBridgeCreatorAddressOverride: tokenBridgeCreator, @@ -406,13 +405,13 @@ describe('createTokenBridge', () => { ], }), value: BigInt(0), - account: l3TokenBridgeDeployer, + account: l3RollupOwner, }); // sign and send the transaction const fundTxRequest = { ...fundTxRequestRaw, chainId: nitroTestnodeL2Client.chain.id }; const fundTxHash = await nitroTestnodeL2Client.sendRawTransaction({ - serializedTransaction: await l3TokenBridgeDeployer.signTransaction(fundTxRequest), + serializedTransaction: await l3RollupOwner.signTransaction(fundTxRequest), }); // get the transaction receipt after waiting for the transaction to complete diff --git a/src/createTokenBridgePrepareTransactionReceipt.ts b/src/createTokenBridgePrepareTransactionReceipt.ts index 6be037db7..ce3be6fc1 100644 --- a/src/createTokenBridgePrepareTransactionReceipt.ts +++ b/src/createTokenBridgePrepareTransactionReceipt.ts @@ -61,6 +61,8 @@ export type WaitForRetryablesResult = [TransactionReceipt, TransactionReceipt]; type GetTokenBridgeContractsParameters = { parentChainPublicClient: PublicClient; + // When the token bridge was deployed through a non-default creator (e.g. a freshly deployed + // one), the inbox->deployment mapping lives on that creator, so it must be queried there. tokenBridgeCreatorAddressOverride?: Address; }; diff --git a/src/decorators/sequencerInboxActions.integration.test.ts b/src/decorators/sequencerInboxActions.integration.test.ts index da802c6e2..2ac05f6c2 100644 --- a/src/decorators/sequencerInboxActions.integration.test.ts +++ b/src/decorators/sequencerInboxActions.integration.test.ts @@ -2,11 +2,11 @@ import { describe, it, expect } from 'vitest'; import { createPublicClient, http, zeroAddress } from 'viem'; import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; -import { nitroTestnodeL2 } from '../chains'; import { getNitroTestnodePrivateKeyAccounts, getInformationFromTestnode, createRollupHelper, + testHelper_getNitroTestnodeL2, } from '../testHelpers'; import { sequencerInboxActions } from './sequencerInboxActions'; import { sequencerInboxABI } from '../contracts/SequencerInbox'; @@ -19,7 +19,7 @@ const { l3SequencerInbox, l3Bridge, l3Rollup, l3BatchPoster, l3UpgradeExecutor } getInformationFromTestnode(); const client = createPublicClient({ - chain: nitroTestnodeL2, + chain: testHelper_getNitroTestnodeL2(), transport: http(), }).extend(sequencerInboxActions({ sequencerInbox: l3SequencerInbox })); diff --git a/src/getKeysets.integration.test.ts b/src/getKeysets.integration.test.ts index e839f4993..943e4e59b 100644 --- a/src/getKeysets.integration.test.ts +++ b/src/getKeysets.integration.test.ts @@ -9,12 +9,12 @@ import { zeroAddress, } from 'viem'; -import { nitroTestnodeL2 } from './chains'; import { sequencerInboxActions } from './decorators/sequencerInboxActions'; import { createRollupHelper, getInformationFromTestnode, getNitroTestnodePrivateKeyAccounts, + testHelper_getNitroTestnodeL2, } from './testHelpers'; import { getKeysets } from './getKeysets'; @@ -22,7 +22,7 @@ const { l3SequencerInbox } = getInformationFromTestnode(); const { l3TokenBridgeDeployer, deployer } = getNitroTestnodePrivateKeyAccounts(); const client = createPublicClient({ - chain: nitroTestnodeL2, + chain: testHelper_getNitroTestnodeL2(), transport: http(), }).extend( sequencerInboxActions({ diff --git a/src/testHelpers.ts b/src/testHelpers.ts index 29123c260..a7bf052c2 100644 --- a/src/testHelpers.ts +++ b/src/testHelpers.ts @@ -1,13 +1,19 @@ -import { Address, Chain, PublicClient, zeroAddress } from 'viem'; +import { Address, Chain, PublicClient, zeroAddress, encodeFunctionData, parseEther } from 'viem'; import { PrivateKeyAccount, privateKeyToAccount, generatePrivateKey } from 'viem/accounts'; +import { erc20ABI } from './contracts/ERC20'; +import { isNonZeroAddress } from './utils/isNonZeroAddress'; import { config } from 'dotenv'; import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; import { generateChainId, sanitizePrivateKey } from './utils'; import { createRollup } from './createRollup'; import { createRollupPrepareDeploymentParamsConfig } from './createRollupPrepareDeploymentParamsConfig'; import { prepareChainConfig } from './prepareChainConfig'; import { CreateRollupParams, RollupCreatorSupportedVersion } from './types/createRollupTypes'; +import { scaleFrom18DecimalsToNativeTokenDecimals } from './utils/decimals'; +import { nitroTestnodeL2 } from './chains'; config(); @@ -191,7 +197,43 @@ function getInformationFromTestnodeContainer(container: string): TestnodeInforma }; } +function getInformationFromConfigDir(configDir: string): TestnodeInformation { + const deploymentJson = JSON.parse( + readFileSync(join(configDir, 'deployment.json'), 'utf8'), + ) as TestnodeDeploymentJson; + const l3DeploymentJson = JSON.parse( + readFileSync(join(configDir, 'l3deployment.json'), 'utf8'), + ) as Required; + const sequencerConfig = JSON.parse( + readFileSync(join(configDir, 'l2-nodeConfig.json'), 'utf8'), + ) as TestnodeNodeConfig; + const l3NodeConfig = JSON.parse( + readFileSync(join(configDir, 'l3-nodeConfig.json'), 'utf8'), + ) as TestnodeNodeConfig; + const l2RollupCreator = l3DeploymentJson['rollup-creator']; + + return { + bridge: deploymentJson['bridge'], + rollup: deploymentJson['rollup'], + sequencerInbox: deploymentJson['sequencer-inbox'], + batchPoster: getBatchPosterAddressFromNodeConfig(sequencerConfig), + l3Bridge: l3DeploymentJson['bridge'], + l3Rollup: l3DeploymentJson['rollup'], + l3SequencerInbox: l3DeploymentJson['sequencer-inbox'], + l3NativeToken: l3DeploymentJson['native-token'], + l3BatchPoster: getBatchPosterAddressFromNodeConfig(l3NodeConfig), + l3UpgradeExecutor: l3DeploymentJson['upgrade-executor'], + l3ChainOwnerUpgradeExecutor: l3DeploymentJson['chain-owner-upgrade-executor'], + ...(l2RollupCreator && l2RollupCreator !== zeroAddress ? { l2RollupCreator } : {}), + }; +} + export function getInformationFromTestnode(): TestnodeInformation { + const configDir = process.env.ARBITRUM_TESTNODE_CONFIG_DIR; + if (configDir) { + return getInformationFromConfigDir(configDir); + } + const containers = Array.from( new Set([ process.env.ARBITRUM_TESTNODE_CONTAINER, @@ -217,6 +259,21 @@ export function getInformationFromTestnode(): TestnodeInformation { throw new Error('testnode container not found'); } +export function testHelper_getNitroTestnodeL2() { + const { l2RollupCreator } = getInformationFromTestnode(); + + if (typeof l2RollupCreator === 'undefined') { + return nitroTestnodeL2; + } + + return { + ...nitroTestnodeL2, + contracts: { + rollupCreator: { address: l2RollupCreator }, + }, + }; +} + export async function createRollupHelper< TRollupCreatorVersion extends RollupCreatorSupportedVersion = 'v3.2', >({ @@ -226,7 +283,6 @@ export async function createRollupHelper< nativeToken = zeroAddress, client, rollupCreatorVersion = testHelper_getRollupCreatorVersionFromEnv() as TRollupCreatorVersion, - rollupCreatorAddressOverride, }: { deployer: PrivateKeyAccountWithPrivateKey; batchPosters: Address[]; @@ -234,18 +290,8 @@ export async function createRollupHelper< nativeToken: Address; client: PublicClient; rollupCreatorVersion?: TRollupCreatorVersion; - rollupCreatorAddressOverride?: Address; }) { const chainId = generateChainId(); - let effectiveRollupCreatorAddressOverride = rollupCreatorAddressOverride; - - if (typeof effectiveRollupCreatorAddressOverride === 'undefined') { - try { - effectiveRollupCreatorAddressOverride = getInformationFromTestnode().l2RollupCreator; - } catch { - // keep SDK defaults when testnode metadata is unavailable - } - } const createRollupConfig = createRollupPrepareDeploymentParamsConfig( client, @@ -263,6 +309,42 @@ export async function createRollupHelper< rollupCreatorVersion, ); + // On the arbitrum-testnode snapshot the custom fee token is held by the L3 rollup owner, not the + // deployer, so fund the deployer to cover the retryable fees paid in the custom fee token. + if (isNonZeroAddress(nativeToken)) { + const { l3RollupOwner } = getNitroTestnodePrivateKeyAccounts(); + // The fee token may have fewer than 18 decimals; scale the funding amount to the token's + // decimals so the transfer doesn't exceed the holder's balance (e.g. on a 6-decimal token). + const nativeTokenDecimals = await client.readContract({ + address: nativeToken, + abi: erc20ABI, + functionName: 'decimals', + }); + const fundRequest = await client.prepareTransactionRequest({ + chain: client.chain, + account: l3RollupOwner, + to: nativeToken, + data: encodeFunctionData({ + abi: erc20ABI, + functionName: 'transfer', + args: [ + deployer.address, + scaleFrom18DecimalsToNativeTokenDecimals({ + amount: parseEther('100000'), + decimals: nativeTokenDecimals, + }), + ], + }), + }); + const fundHash = await client.sendRawTransaction({ + serializedTransaction: await l3RollupOwner.signTransaction({ + ...fundRequest, + chainId: client.chain!.id, + }), + }); + await client.waitForTransactionReceipt({ hash: fundHash }); + } + const createRollupInformation = rollupCreatorVersion === 'v2.1' ? await createRollup({ @@ -275,7 +357,6 @@ export async function createRollupHelper< account: deployer, parentChainPublicClient: client, rollupCreatorVersion: 'v2.1', - rollupCreatorAddressOverride: effectiveRollupCreatorAddressOverride, }) : await createRollup({ params: { @@ -287,7 +368,6 @@ export async function createRollupHelper< account: deployer, parentChainPublicClient: client, rollupCreatorVersion: 'v3.2', - rollupCreatorAddressOverride: effectiveRollupCreatorAddressOverride, }); // create test rollup with ETH as gas token diff --git a/src/utils/getRollupCreatorAddress.ts b/src/utils/getRollupCreatorAddress.ts index 7b431f093..1466ac415 100644 --- a/src/utils/getRollupCreatorAddress.ts +++ b/src/utils/getRollupCreatorAddress.ts @@ -11,18 +11,17 @@ export function getRollupCreatorAddress( rollupCreatorVersion: RollupCreatorSupportedVersion = 'v3.2', ): Address { const { chainId: parentChainId, isCustom: parentChainIsCustom } = validateParentChain(client); + const configuredAddress = (client.chain?.contracts?.rollupCreator as ChainContract | undefined) + ?.address; - if (parentChainIsCustom) { - const contract = client.chain?.contracts?.rollupCreator as ChainContract | undefined; - const address = contract?.address; - - if (typeof address === 'undefined') { - throw new Error( - `Address for RollupCreator is missing on custom parent chain with id ${parentChainId}`, - ); - } + if (typeof configuredAddress !== 'undefined') { + return configuredAddress; + } - return address; + if (parentChainIsCustom) { + throw new Error( + `Address for RollupCreator is missing on custom parent chain with id ${parentChainId}`, + ); } const rollupCreatorAddress = diff --git a/src/utils/getRollupCreatorAddress.unit.test.ts b/src/utils/getRollupCreatorAddress.unit.test.ts index 902cd6494..8d112fdae 100644 --- a/src/utils/getRollupCreatorAddress.unit.test.ts +++ b/src/utils/getRollupCreatorAddress.unit.test.ts @@ -38,6 +38,22 @@ it(`successfully returns address for Sepolia (v2.1)`, () => { ); }); +it(`prefers a RollupCreator configured on a supported chain`, () => { + const rollupCreator = '0x31421C442c422BD16aef6ae44D3b11F404eeaBd9'; + const client = createPublicClient({ + chain: { + ...sepolia, + contracts: { + ...sepolia.contracts, + rollupCreator: { address: rollupCreator }, + }, + }, + transport: http(), + }); + + expect(getRollupCreatorAddress(client)).toEqual(rollupCreator); +}); + it(`fails to return address for an unrecognized parent chain`, () => { const chain = testHelper_createCustomParentChain(); diff --git a/wagmi.config.ts b/wagmi.config.ts index fc7a168ca..56c735aa0 100644 --- a/wagmi.config.ts +++ b/wagmi.config.ts @@ -101,7 +101,7 @@ const contracts: ContractConfig[] = [ [baseSepolia.id]: '0x8d1668636D053C10F57367D68118bD624f41ffe6', // local nitro-testnode (on "release" branch with --tokenbridge --l3node --l3-token-bridge flags) [nitroTestnodeL1.id]: '0xe6D50099f4d891240435143193d46581A1447202', - [nitroTestnodeL2.id]: '0xbcF51F3AAb5D5Efa025b4A2B235BDc9F3f69b4d2', + [nitroTestnodeL2.id]: '0x1E08B9c3f94E9aBcc531f67F949d796eC76963b9', }, }, {