diff --git a/README.md b/README.md index 09dd971682..a787f212a5 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Below is an overview of the Arbitrum SDK functionality. See the [tutorials](http - ##### Deposit Ether Into Arbitrum ```ts -import { getL2Network, EthBridger } from '@arbitrum/sdk' +import { getArbitrumNetwork, EthBridger } from '@arbitrum/sdk' -const l2Network = await getL2Network( +const l2Network = await getArbitrumNetwork( l2ChainID /** <-- chain id of target Arbitrum chain */ ) const ethBridger = new EthBridger(l2Network) diff --git a/scripts/deployStandard.ts b/scripts/deployStandard.ts index 78133b305e..21fb478986 100644 --- a/scripts/deployStandard.ts +++ b/scripts/deployStandard.ts @@ -1,11 +1,11 @@ import { instantiateBridge } from './instantiate_bridge' import dotenv from 'dotenv' import args from './getCLargs' -import { constants, BigNumber, utils } from 'ethers' +import { constants, BigNumber } from 'ethers' +import { ZeroAddress, formatEther } from 'ethers-v6' import { MultiCaller } from '../src' import axios from 'axios' import prompt from 'prompts' -import { ZeroAddress } from 'ethers-v6' dotenv.config() const privKey = process.env.PRIVKEY as string @@ -156,10 +156,10 @@ const main = async () => { const fee = price.mul(gasNeeded) if (fee.gt(walletBal)) { console.log( - `An estimated ${utils.formatEther( - fee - )} ether is needed for deposit; you only have ${utils.formatEther( - walletBal + `An estimated ${formatEther( + fee.toHexString() + )} ether is needed for deposit; you only have ${formatEther( + walletBal.toHexString() )} ether. Will try depositing anyway:` ) } diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index 7dbb1ce2fd..d0b7fdff45 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -2,10 +2,14 @@ import * as dotenv from 'dotenv' dotenv.config() import { execSync } from 'child_process' import * as fs from 'fs' -import { L2Network } from '../src' -import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' + import { IERC20Bridge__factory } from '../src/lib/abi/factories/IERC20Bridge__factory' import { ethers } from 'ethers' +import { + L2Network, + ArbitrumNetwork, + mapL2NetworkToArbitrumNetwork, +} from '../src/lib/dataEntities/networks' const isTestingOrbitChains = process.env.ORBIT_TEST === '1' @@ -39,24 +43,29 @@ async function patchNetworks( l2Network: L2Network, l3Network: L2Network | undefined, l2Provider: ethers.providers.Provider | undefined -) { - l2Network.parentChainId = (l2Network as any).partnerChainID - l2Network.blockTime = ARB_MINIMUM_BLOCK_TIME_IN_SECONDS +): Promise<{ + patchedL2Network: ArbitrumNetwork + patchedL3Network?: ArbitrumNetwork +}> { + const patchedL2Network = mapL2NetworkToArbitrumNetwork(l2Network) // native token for l3 if (l3Network && l2Provider) { - l3Network.parentChainId = (l3Network as any).partnerChainID - l3Network.blockTime = ARB_MINIMUM_BLOCK_TIME_IN_SECONDS + const patchedL3Network = mapL2NetworkToArbitrumNetwork(l3Network) try { - l3Network.nativeToken = await IERC20Bridge__factory.connect( + patchedL3Network.nativeToken = await IERC20Bridge__factory.connect( l3Network.ethBridge.bridge, l2Provider ).nativeToken() } catch (e) { // l3 network doesn't have a native token } + + return { patchedL2Network, patchedL3Network } } + + return { patchedL2Network } } async function main() { @@ -66,17 +75,24 @@ async function main() { if (isTestingOrbitChains) { const { l2Network: l3Network } = getLocalNetworksFromContainer('l2l3') - await patchNetworks( + const { patchedL2Network, patchedL3Network } = await patchNetworks( output.l2Network, l3Network, new ethers.providers.JsonRpcProvider(process.env['ARB_URL']) ) + output = { - l1Network: output.l2Network, - l2Network: l3Network, + l1Network: patchedL2Network, + l2Network: patchedL3Network, } } else { - await patchNetworks(output.l2Network, undefined, undefined) + const { patchedL2Network } = await patchNetworks( + output.l2Network, + undefined, + undefined + ) + + output.l2Network = patchedL2Network } fs.writeFileSync('localNetwork.json', JSON.stringify(output, null, 2)) diff --git a/scripts/instantiate_bridge.ts b/scripts/instantiate_bridge.ts index af752bc640..747a81e8b6 100644 --- a/scripts/instantiate_bridge.ts +++ b/scripts/instantiate_bridge.ts @@ -23,14 +23,11 @@ import dotenv from 'dotenv' import args from './getCLargs' import { EthBridger, InboxTools, Erc20Bridger } from '../src' import { - l1Networks, - l2Networks, - L1Network, - L2Network, + ArbitrumNetwork, + getArbitrumNetwork, } from '../src/lib/dataEntities/networks' import { Signer } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' -import { isDefined } from '../src/lib/utils/lib' dotenv.config() @@ -39,19 +36,18 @@ const ethKey = process.env['ETH_KEY'] as string const defaultNetworkId = 421614 -export const instantiateBridge = ( +export const instantiateBridge = async ( l1PkParam?: string, l2PkParam?: string -): { - l1Network: L1Network - l2Network: L2Network +): Promise<{ + l2Network: ArbitrumNetwork l1Signer: Signer l2Signer: Signer erc20Bridger: Erc20Bridger ethBridger: EthBridger adminErc20Bridger: AdminErc20Bridger inboxTools: InboxTools -} => { +}> => { if (!l1PkParam && !ethKey) { throw new Error('need ARB_KEY var') } @@ -68,21 +64,8 @@ export const instantiateBridge = ( l2NetworkID = defaultNetworkId } - const isL1 = isDefined(l1Networks[l2NetworkID]) - const isL2 = isDefined(l2Networks[l2NetworkID]) - if (!isL1 && !isL2) { - throw new Error(`Unrecognized network ID: ${l2NetworkID}`) - } - if (!isL2) { - throw new Error(`Tests must specify an L2 network ID: ${l2NetworkID}`) - } - const l2Network = l2Networks[l2NetworkID] - const l1Network = l1Networks[l2Network.parentChainId] - - if (!l1Network) { - throw new Error(`Unrecognised parent chain id: ${l2Network.parentChainId}`) - } + const l2Network = await getArbitrumNetwork(l2NetworkID) const l1Rpc = (() => { if (l2NetworkID === 42161) return process.env['MAINNET_RPC'] as string @@ -138,7 +121,6 @@ export const instantiateBridge = ( const inboxTools = new InboxTools(l1Signer, l2Network) return { - l1Network, l2Network, l1Signer, l2Signer, diff --git a/scripts/sendL2SignedMsg.ts b/scripts/sendL2SignedMsg.ts index d117247518..08ccc8966f 100644 --- a/scripts/sendL2SignedMsg.ts +++ b/scripts/sendL2SignedMsg.ts @@ -18,11 +18,11 @@ import { BigNumber } from 'ethers' import { InboxTools } from '../src/lib/inbox/inbox' -import { getL2Network } from '../src/lib/dataEntities/networks' +import { getArbitrumNetwork } from '../src/lib/dataEntities/networks' import { testSetup } from '../scripts/testSetup' const sendSignedMsg = async () => { const { l1Deployer, l2Deployer } = await testSetup() - const l2Network = await getL2Network(await l2Deployer.getChainId()) + const l2Network = await getArbitrumNetwork(await l2Deployer.getChainId()) const inbox = new InboxTools(l1Deployer, l2Network) const message = { to: await l2Deployer.getAddress(), diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 97c0ca3638..a7a05d05c9 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -23,11 +23,11 @@ import dotenv from 'dotenv' import { EthBridger, InboxTools, Erc20Bridger } from '../src' import { - L1Network, + L2Network, ArbitrumNetwork, - getL1Network, + mapL2NetworkToArbitrumNetwork, getArbitrumNetwork, - addCustomNetwork, + addCustomArbitrumNetwork, } from '../src/lib/dataEntities/networks' import { Signer } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' @@ -72,7 +72,6 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { } export const testSetup = async (): Promise<{ - parentChain: L1Network | ArbitrumNetwork childChain: ArbitrumNetwork parentSigner: Signer childSigner: Signer @@ -95,53 +94,18 @@ export const testSetup = async (): Promise<{ const parentSigner = seed.connect(ethProvider) const childSigner = seed.connect(arbProvider) - let setParentChain: L1Network | ArbitrumNetwork, - setChildChain: ArbitrumNetwork + let setChildChain: ArbitrumNetwork + try { - const l1Network = isTestingOrbitChains - ? await getArbitrumNetwork(parentDeployer) - : await getL1Network(parentDeployer) const l2Network = await getArbitrumNetwork(childDeployer) - setParentChain = l1Network setChildChain = l2Network } catch (err) { // the networks havent been added yet - // check if theres an existing network available - const localNetworkFile = getLocalNetworksFromFile() - - const { l1Network: parentChain, l2Network: childChain } = localNetworkFile - - if (isTestingOrbitChains) { - const _parentChain = parentChain as ArbitrumNetwork - const ethLocal: L1Network = { - blockTime: 10, - chainID: _parentChain.parentChainId, - isCustom: true, - name: 'EthLocal', - isArbitrum: false, - } - - addCustomNetwork({ - customL1Network: ethLocal, - customArbitrumNetwork: _parentChain, - }) - - addCustomNetwork({ - customArbitrumNetwork: childChain, - }) - - setParentChain = parentChain - setChildChain = childChain - } else { - addCustomNetwork({ - customL1Network: parentChain as L1Network, - customArbitrumNetwork: childChain, - }) - - setParentChain = parentChain - setChildChain = childChain - } + const { l2Network: childChain } = getLocalNetworksFromFile() + + addCustomArbitrumNetwork(childChain) + setChildChain = childChain } const erc20Bridger = new Erc20Bridger(setChildChain) @@ -160,7 +124,6 @@ export const testSetup = async (): Promise<{ childSigner, parentProvider: ethProvider, childProvider: arbProvider, - parentChain: setParentChain, childChain: setChildChain, erc20Bridger, adminErc20Bridger, @@ -172,7 +135,6 @@ export const testSetup = async (): Promise<{ } export function getLocalNetworksFromFile(): { - l1Network: L1Network | ArbitrumNetwork l2Network: ArbitrumNetwork } { const pathToLocalNetworkFile = path.join(__dirname, '..', 'localNetwork.json') @@ -180,5 +142,9 @@ export function getLocalNetworksFromFile(): { throw new ArbSdkError('localNetwork.json not found, must gen:network first') } const localNetworksFile = fs.readFileSync(pathToLocalNetworkFile, 'utf8') - return JSON.parse(localNetworksFile) + const localL2: L2Network = JSON.parse(localNetworksFile).l2Network + + return { + l2Network: mapL2NetworkToArbitrumNetwork(localL2), + } } diff --git a/src/index.ts b/src/index.ts index c0c82efc0e..9ab26c544b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,13 +43,9 @@ export { ParentToChildMessageGasEstimator } from './lib/message/ParentToChildMes export { argSerializerConstructor } from './lib/utils/byte_serialize_params' export { CallInput, MultiCaller } from './lib/utils/multicall' export { - L1Networks, - L2Networks, - L1Network, - L2Network, - getL1Network, - getL2Network, - addCustomNetwork, + ArbitrumNetwork, + getArbitrumNetwork, + addCustomArbitrumNetwork, addDefaultLocalNetwork, getChildrenForNetwork, } from './lib/dataEntities/networks' diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index bc3f74bcd8..b803e3dc31 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -20,11 +20,7 @@ import { ZeroAddress } from 'ethers-v6' import { ParentContractTransaction } from '../message/ParentTransaction' import { ChildContractTransaction } from '../message/ChildTransaction' -import { - L1Network, - ArbitrumNetwork, - getParentForNetwork, -} from '../dataEntities/networks' +import { ArbitrumNetwork } from '../dataEntities/networks' import { SignerOrProvider, SignerProviderUtils, @@ -34,11 +30,6 @@ import { * Base for bridging assets from parent-to-child and back */ export abstract class AssetBridger { - /** - * Parent chain for the given Arbitrum chain, can be an L1 or an L2 - */ - public readonly parentChain: L1Network | ArbitrumNetwork - /** * In case of a chain that uses ETH as its native/gas token, this is either `undefined` or the zero address * @@ -47,7 +38,6 @@ export abstract class AssetBridger { public readonly nativeToken?: string public constructor(public readonly childChain: ArbitrumNetwork) { - this.parentChain = getParentForNetwork(childChain) this.nativeToken = childChain.nativeToken } @@ -56,7 +46,10 @@ export abstract class AssetBridger { * @param sop */ protected async checkParentChain(sop: SignerOrProvider): Promise { - await SignerProviderUtils.checkNetworkMatches(sop, this.parentChain.chainID) + await SignerProviderUtils.checkNetworkMatches( + sop, + this.childChain.parentChainId + ) } /** @@ -64,7 +57,7 @@ export abstract class AssetBridger { * @param sop */ protected async checkChildChain(sop: SignerOrProvider): Promise { - await SignerProviderUtils.checkNetworkMatches(sop, this.childChain.chainID) + await SignerProviderUtils.checkNetworkMatches(sop, this.childChain.chainId) } /** diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 87e452b07f..f787b00365 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -66,7 +66,7 @@ import { ChildToParentTransactionRequest, ParentToChildTransactionRequest, } from '../dataEntities/transactionRequest' -import { defaultAbiCoder } from 'ethers/lib/utils' +import { AbiCoder } from 'ethers-v6' import { OmitTyped, RequiredPick } from '../utils/types' import { RetryableDataTools } from '../dataEntities/retryableData' import { EventArgs } from '../dataEntities/event' @@ -595,26 +595,29 @@ export class Erc20Bridger extends AssetBridger< depositParams: OmitTyped ) { if (!this.nativeTokenIsEth) { - return defaultAbiCoder.encode( + return AbiCoder.defaultAbiCoder().encode( ['uint256', 'bytes', 'uint256'], [ // maxSubmissionCost - depositParams.maxSubmissionCost, // will be zero + BigInt(depositParams.maxSubmissionCost.toHexString()), // will be zero // callHookData '0x', // nativeTokenTotalFee - depositParams.gasLimit - .mul(depositParams.maxFeePerGas) - .add(depositParams.maxSubmissionCost), // will be zero + BigInt( + depositParams.gasLimit + .mul(depositParams.maxFeePerGas) + .add(depositParams.maxSubmissionCost) + .toHexString() + ), // will be zero ] ) } - return defaultAbiCoder.encode( + return AbiCoder.defaultAbiCoder().encode( ['uint256', 'bytes'], [ // maxSubmissionCost - depositParams.maxSubmissionCost, + BigInt(depositParams.maxSubmissionCost.toHexString()), // callHookData '0x', ] diff --git a/src/lib/dataEntities/address.ts b/src/lib/dataEntities/address.ts index 1a3361e17f..70cb3263fd 100644 --- a/src/lib/dataEntities/address.ts +++ b/src/lib/dataEntities/address.ts @@ -1,5 +1,4 @@ -import { getAddress } from 'ethers-v6' -import { utils } from 'ethers' +import { getAddress, isAddress } from 'ethers-v6' import { ADDRESS_ALIAS_OFFSET } from './constants' import { ArbSdkError } from './errors' @@ -16,7 +15,7 @@ export class Address { * @param value A valid Ethereum address. Doesn't need to be checksum cased. */ constructor(public readonly value: string) { - if (!utils.isAddress(value)) + if (!isAddress(value)) throw new ArbSdkError(`'${value}' is not a valid address`) } diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 4af9fbb072..019015ba7b 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -16,42 +16,41 @@ /* eslint-env node */ ;('use strict') +import { Provider } from '@ethersproject/abstract-provider' + import { SignerOrProvider, SignerProviderUtils } from './signerOrProvider' import { ArbSdkError } from '../dataEntities/errors' -import { - ARB1_NITRO_GENESIS_L2_BLOCK, - ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, -} from './constants' +import { ARB1_NITRO_GENESIS_L2_BLOCK } from './constants' import { RollupAdminLogic__factory } from '../abi/factories/RollupAdminLogic__factory' - -export interface Network { - chainID: number - name: string - isCustom: boolean - /** - * Minimum possible block time for the chain (in seconds). - */ - blockTime: number -} - -/** - * Represents an L1 chain, e.g. Ethereum Mainnet or Sepolia. - */ -export interface L1Network extends Network { - isArbitrum: false -} +import { Prettify } from '../utils/types' /** * Represents an Arbitrum chain, e.g. Arbitrum One, Arbitrum Sepolia, or an L3 chain. */ -export interface ArbitrumNetwork extends Network { - tokenBridge: TokenBridge - ethBridge: EthBridge +export interface ArbitrumNetwork { + /** + * Name of the chain. + */ + name: string + /** + * Id of the chain. + */ + chainId: number /** * Chain id of the parent chain, i.e. the chain on which this chain settles to. */ parentChainId: number - isArbitrum: true + /** + * The core contracts + */ + ethBridge: EthBridge + /** + * The token bridge contracts. + */ + tokenBridge: TokenBridge + /** + * The time allowed for validators to dispute or challenge state assertions. Measured in L1 blocks. + */ confirmPeriodBlocks: number /** * Represents how long a retryable ticket lasts for before it expires (in seconds). Defaults to 7 days. @@ -63,8 +62,26 @@ export interface ArbitrumNetwork extends Network { * In case of a chain that uses an ERC-20 token from the parent chain as its native/gas token, this is the address of said token on the parent chain */ nativeToken?: string + /** + * Whether or not the chain was registered by the user. + */ + isCustom: boolean } +/** + * This type is only here for when you want to achieve backwards compatibility between SDK v3 and v4. + * + * Please see {@link ArbitrumNetwork} for the latest type. + * + * @deprecated since v4 + */ +export type L2Network = Prettify< + Omit & { + chainID: number + partnerChainID: number + } +> + export interface TokenBridge { l1GatewayRouter: string l2GatewayRouter: string @@ -93,16 +110,8 @@ export interface EthBridge { } } -export interface L1Networks { - [id: string]: L1Network -} - -export interface ArbitrumNetworks { - [id: string]: ArbitrumNetwork -} - export interface Networks { - [id: string]: L1Network | ArbitrumNetwork + [id: string]: ArbitrumNetwork } const mainnetTokenBridge: TokenBridge = { @@ -138,47 +147,17 @@ const mainnetETHBridge: EthBridge = { * Storage for all networks, either L1, L2 or L3. */ export const networks: Networks = { - 1: { - chainID: 1, - name: 'Mainnet', - blockTime: 14, - isCustom: false, - isArbitrum: false, - }, - 1338: { - chainID: 1338, - name: 'Hardhat_Mainnet_Fork', - blockTime: 1, - isCustom: false, - isArbitrum: false, - }, - 11155111: { - chainID: 11155111, - name: 'Sepolia', - blockTime: 12, - isCustom: false, - isArbitrum: false, - }, - 17000: { - chainID: 17000, - name: 'Holesky', - blockTime: 12, - isCustom: false, - isArbitrum: false, - }, 42161: { - chainID: 42161, + chainId: 42161, name: 'Arbitrum One', parentChainId: 1, - isArbitrum: true, tokenBridge: mainnetTokenBridge, ethBridge: mainnetETHBridge, confirmPeriodBlocks: 45818, isCustom: false, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, }, 42170: { - chainID: 42170, + chainId: 42170, confirmPeriodBlocks: 45818, ethBridge: { bridge: '0xC1Ebd02f738644983b6C4B2d440b8e77DdE276Bd', @@ -187,7 +166,6 @@ export const networks: Networks = { rollup: '0xFb209827c58283535b744575e11953DCC4bEAD88', sequencerInbox: '0x211E1c4c7f1bF5351Ac850Ed10FD68CFfCF6c21b', }, - isArbitrum: true, isCustom: false, name: 'Arbitrum Nova', parentChainId: 1, @@ -207,10 +185,9 @@ export const networks: Networks = { l2Weth: '0x722E8BdD2ce80A4422E880164f2079488e115365', l2WethGateway: '0x7626841cB6113412F9c88D3ADC720C9FAC88D9eD', }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, }, 421614: { - chainID: 421614, + chainId: 421614, confirmPeriodBlocks: 20, ethBridge: { bridge: '0x38f918D0E9F1b721EDaA41302E399fa1B79333a9', @@ -219,7 +196,6 @@ export const networks: Networks = { rollup: '0xd80810638dbDF9081b72C1B33c65375e807281C8', sequencerInbox: '0x6c97864CE4bEf387dE0b3310A44230f7E3F1be0D', }, - isArbitrum: true, isCustom: false, name: 'Arbitrum Rollup Sepolia Testnet', parentChainId: 11155111, @@ -239,10 +215,9 @@ export const networks: Networks = { l2Weth: '0x980B62Da83eFf3D4576C647993b0c1D7faf17c73', l2WethGateway: '0xCFB1f08A4852699a979909e22c30263ca249556D', }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, }, 23011913: { - chainID: 23011913, + chainId: 23011913, confirmPeriodBlocks: 20, ethBridge: { bridge: '0x35aa95ac4747D928E2Cd42FE4461F6D9d1826346', @@ -251,7 +226,6 @@ export const networks: Networks = { rollup: '0x94db9E36d9336cD6F9FfcAd399dDa6Cc05299898', sequencerInbox: '0x00A0F15b79d1D3e5991929FaAbCF2AA65623530c', }, - isArbitrum: true, isCustom: false, name: 'Stylus Testnet', parentChainId: 421614, @@ -271,20 +245,19 @@ export const networks: Networks = { l2Weth: '0x61Dc4b961D2165623A25EB775260785fE78BD37C', l2WethGateway: '0x7021B4Edd9f047772242fc948441d6e0b9121175', }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, }, } /** * Determines if a chain is a parent of *any* other chain. Could be an L1 or an L2 chain. */ -export const isParentChain = ( - parentChainOrChainId: L1Network | ArbitrumNetwork | number +export const isParentNetwork = ( + parentChainOrChainId: ArbitrumNetwork | number ): boolean => { const parentChainId = typeof parentChainOrChainId === 'number' ? parentChainOrChainId - : parentChainOrChainId.chainID + : parentChainOrChainId.chainId // Check if there are any chains that have this chain as its parent chain return [...Object.values(l2Networks)].some( @@ -292,150 +265,57 @@ export const isParentChain = ( ) } -/** - * Determines if a chain is an Arbitrum chain. Could be an L2 or an L3 chain. - */ -const isArbitrumNetwork = ( - chain: L1Network | ArbitrumNetwork -): chain is ArbitrumNetwork => { - return chain.isArbitrum -} - -/** - * Determines if a chain is specifically an L1 chain (not L2 or L3). - */ -export const isL1Network = ( - chain: L1Network | ArbitrumNetwork -): chain is L1Network => { - return !chain.isArbitrum -} - -/** - * Builds an object that is a list of chains filtered by the provided predicate function indexed by their chain id - * @param filterFn - A predicate function to determine if a chain should be included. - * @return An object with only the filtered chains. - */ -const getChainsByType = ( - filterFn: (chain: L1Network | ArbitrumNetwork) => boolean -): T => { - return Object.entries(networks).reduce( - (accumulator, [chainId, chainData]) => { - if (filterFn(chainData)) { - accumulator[chainId] = chainData - } - return accumulator - }, - {} - ) as T -} - -const getL1Chains = () => getChainsByType(isL1Network) -const getArbitrumChains = () => - getChainsByType(isArbitrumNetwork) - -/** - * Returns the parent chain for the given chain. - */ -export const getParentForNetwork = (chain: L1Network | ArbitrumNetwork) => { - if (!isArbitrumNetwork(chain)) { - throw new ArbSdkError(`Chain ${chain.chainID} is not an Arbitrum chain.`) - } - - const parentChain: L1Network | ArbitrumNetwork | undefined = - networks[chain.parentChainId] - - if (!parentChain || !isParentChain(parentChain)) { - throw new ArbSdkError( - `Parent chain ${chain.parentChainId} not recognized for chain ${chain.chainID}.` - ) - } - - return parentChain -} +const getArbitrumChains = () => networks /** * Returns a list of children chains for the given chain or chain id. */ export const getChildrenForNetwork = ( - parentChainOrChainId: L1Network | ArbitrumNetwork | number + parentChainOrChainId: ArbitrumNetwork | number ): ArbitrumNetwork[] => { const parentChainId = typeof parentChainOrChainId === 'number' ? parentChainOrChainId - : parentChainOrChainId.chainID + : parentChainOrChainId.chainId return Object.values(getArbitrumChains()).filter( arbitrumChain => arbitrumChain.parentChainId === parentChainId ) } -/** - * Index of *only* L1 chains that have been added. - */ -export let l1Networks: L1Networks = getL1Chains() - /** * Index of all Arbitrum chains that have been added. */ -export let l2Networks: ArbitrumNetworks = getArbitrumChains() +export let l2Networks = getArbitrumChains() /** - * Returns the network associated with the given Signer, Provider or chain id. - * @note Throws if the chain is not recognized. + * Returns the Arbitrum chain associated with the given signer, provider or chain id. + * + * @note Throws if the chain is not an Arbitrum chain. */ -export const getNetwork = async ( - signerOrProviderOrChainID: SignerOrProvider | number, - layer: 1 | 2 -) => { - const chainID = await (async () => { - if (typeof signerOrProviderOrChainID === 'number') { - return signerOrProviderOrChainID +export const getArbitrumNetwork = async ( + signerOrProviderOrChainId: SignerOrProvider | number +): Promise => { + const chainId = await (async () => { + if (typeof signerOrProviderOrChainId === 'number') { + return signerOrProviderOrChainId } const provider = SignerProviderUtils.getProviderOrThrow( - signerOrProviderOrChainID + signerOrProviderOrChainId ) - const { chainId } = await provider.getNetwork() - return chainId + return (await provider.getNetwork()).chainId })() - let network: L1Network | ArbitrumNetwork | undefined = undefined - - if (layer === 1) { - network = getL1Chains()[chainID] - } else { - network = getArbitrumChains()[chainID] - } + const network: ArbitrumNetwork | undefined = getArbitrumChains()[chainId] if (!network) { - throw new ArbSdkError(`Unrecognized network ${chainID}.`) + throw new ArbSdkError(`Unrecognized network ${chainId}.`) } return network } -/** - * Returns the L1 chain associated with the given signer, provider or chain id. - * - * @note Throws if the chain is not an L1 chain. - */ -export const getL1Network = ( - signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { - return getNetwork(signerOrProviderOrChainID, 1) as Promise -} - -/** - * Returns the Arbitrum chain associated with the given signer, provider or chain id. - * - * @note Throws if the chain is not an Arbitrum chain. - */ -export const getArbitrumNetwork = ( - signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { - return getNetwork(signerOrProviderOrChainID, 2) as Promise -} - /** * Returns the addresses of all contracts that make up the ETH bridge * @param rollupContractAddress Address of the Rollup contract @@ -468,82 +348,18 @@ export const getEthBridgeInformation = async ( } /** - * Adds any chain to the global index of networks and updates the parent/child relationships. - */ -const addNetwork = (network: L1Network | ArbitrumNetwork) => { - // store the network with the rest of the networks - networks[network.chainID] = network - - // if it's a parent chain (L1 or L2), assign it as parent to all the children - if (isParentChain(network)) { - const children = getChildrenForNetwork(network) - - children.forEach(child => { - child.parentChainId = network.chainID - }) - } - - // if it's an arbitrum chain, add it to the parent's list of children - if (isArbitrumNetwork(network)) { - const parent: L1Network | ArbitrumNetwork | undefined = - networks[network.parentChainId] - - if (!parent) { - throw new ArbSdkError( - `Network ${network.chainID}'s parent network ${network.parentChainId} is not recognized` - ) - } - } - - l1Networks = getL1Chains() - l2Networks = getArbitrumChains() -} - -/** - * Registers a pair of custom L1 and L2 chains, or a single custom Arbitrum chain (L2 or L3). + * Registers a custom Arbitrum chain (L2 or L3). * - * @param customL1Network the custom L1 chain (optional) - * @param customArbitrumNetwork the custom L2 or L3 chain + * @param network */ -export const addCustomNetwork = ({ - customL1Network, - customArbitrumNetwork, -}: { - customL1Network?: L1Network - customArbitrumNetwork: ArbitrumNetwork -}): void => { - if (customL1Network) { - if (customL1Network.chainID !== customArbitrumNetwork.parentChainId) { - throw new ArbSdkError( - `Partner chain id for Arbitrum network ${customArbitrumNetwork.chainID} doesn't match the provided L1 network. Expected ${customL1Network.chainID} but got ${customArbitrumNetwork.parentChainId}.` - ) - } - - // check the if the parent chain is in any of the lists - if (l1Networks[customL1Network.chainID]) { - throw new ArbSdkError( - `Network ${customL1Network.chainID} already included` - ) - } else if (!customL1Network.isCustom) { - throw new ArbSdkError( - `Custom network ${customL1Network.chainID} must have isCustom flag set to true` - ) - } - - addNetwork(customL1Network) +export const addCustomArbitrumNetwork = (network: ArbitrumNetwork): void => { + if (typeof networks[network.chainId] !== 'undefined') { + throw new Error(`Network ${network.chainId} already included`) } - if (l2Networks[customArbitrumNetwork.chainID]) { - throw new ArbSdkError( - `Network ${customArbitrumNetwork.chainID} already included` - ) - } else if (!customArbitrumNetwork.isCustom) { - throw new ArbSdkError( - `Custom network ${customArbitrumNetwork.chainID} must have isCustom flag set to true` - ) - } - - addNetwork(customArbitrumNetwork) + // store the network with the rest of the networks + networks[network.chainId] = network + l2Networks = getArbitrumChains() } /** @@ -551,20 +367,9 @@ export const addCustomNetwork = ({ * * @see {@link https://github.com/OffchainLabs/nitro} */ -export const addDefaultLocalNetwork = (): { - l1Network: L1Network - l2Network: ArbitrumNetwork -} => { - const defaultLocalL1Network: L1Network = { - blockTime: 10, - chainID: 1337, - isCustom: true, - name: 'EthLocal', - isArbitrum: false, - } - +export const addDefaultLocalNetwork = (): ArbitrumNetwork => { const defaultLocalL2Network: ArbitrumNetwork = { - chainID: 412346, + chainId: 412346, confirmPeriodBlocks: 20, ethBridge: { bridge: '0x2b360A9881F21c3d7aa0Ea6cA0De2a3341d4eF3C', @@ -573,7 +378,6 @@ export const addDefaultLocalNetwork = (): { rollup: '0x65a59D67Da8e710Ef9A01eCa37f83f84AEdeC416', sequencerInbox: '0xE7362D0787b51d8C72D504803E5B1d6DcdA89540', }, - isArbitrum: true, isCustom: true, name: 'ArbLocal', parentChainId: 1337, @@ -593,18 +397,11 @@ export const addDefaultLocalNetwork = (): { l2Weth: '0x408Da76E87511429485C32E4Ad647DD14823Fdc4', l2WethGateway: '0x4A2bA922052bA54e29c5417bC979Daaf7D5Fe4f4', }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, } - addCustomNetwork({ - customL1Network: defaultLocalL1Network, - customArbitrumNetwork: defaultLocalL2Network, - }) + addCustomArbitrumNetwork(defaultLocalL2Network) - return { - l1Network: defaultLocalL1Network, - l2Network: defaultLocalL2Network, - } + return defaultLocalL2Network } /** @@ -617,7 +414,6 @@ const createNetworkStateHandler = () => { resetNetworksToDefault: () => { Object.keys(networks).forEach(key => delete networks[key]) Object.assign(networks, JSON.parse(JSON.stringify(initialState))) - l1Networks = getL1Chains() l2Networks = getArbitrumChains() }, } @@ -629,7 +425,7 @@ export function getNitroGenesisBlock( const arbitrumChainId = typeof arbitrumChainOrChainId === 'number' ? arbitrumChainOrChainId - : arbitrumChainOrChainId.chainID + : arbitrumChainOrChainId.chainId // all networks except Arbitrum One started off with Nitro if (arbitrumChainId === 42161) { @@ -639,13 +435,53 @@ export function getNitroGenesisBlock( return 0 } -const { resetNetworksToDefault } = createNetworkStateHandler() +export async function getMulticallAddress( + providerOrChainId: Provider | number +): Promise { + const chains = [...Object.values(l2Networks)] + + const chainId = + typeof providerOrChainId === 'number' + ? providerOrChainId + : (await providerOrChainId.getNetwork()).chainId + const chain = chains.find(c => c.chainId === chainId) + + // The provided chain is found in the list + if (typeof chain !== 'undefined') { + // Return the address of Multicall on the chain + return chain.tokenBridge.l2Multicall + } -export { resetNetworksToDefault } -export const getChildChain = getArbitrumNetwork + // The provided chain is not found in the list + // Try to find a chain that references this chain as its parent + const child = chains.find(c => c.parentChainId === chainId) -export { - ArbitrumNetwork as L2Network, - ArbitrumNetworks as L2Networks, - getArbitrumNetwork as getL2Network, + // No chains reference this chain as its parent + if (typeof child === 'undefined') { + throw new Error( + `Failed to retrieve Multicall address for chain: ${chainId}` + ) + } + + // Return the address of Multicall on the parent chain + return child.tokenBridge.l1MultiCall } + +/** + * Maps the old {@link L2Network} (from SDK v3) to {@link ArbitrumNetwork} (from SDK v4). + */ +export function mapL2NetworkToArbitrumNetwork( + l2Network: L2Network +): ArbitrumNetwork { + return { + // Spread properties + ...l2Network, + // Map properties that were changed + chainId: l2Network.chainID, + parentChainId: l2Network.partnerChainID, + } +} + +const { resetNetworksToDefault } = createNetworkStateHandler() + +export { resetNetworksToDefault } diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index a887aa9094..dcf46148bb 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -29,11 +29,7 @@ import { SequencerInbox__factory } from '../abi/factories/SequencerInbox__factor import { IInbox__factory } from '../abi/factories/IInbox__factory' import { RequiredPick } from '../utils/types' import { MessageDeliveredEvent } from '../abi/Bridge' -import { - L2Network as ChildChain, - L1Network as ParentChain, - getParentForNetwork, -} from '../dataEntities/networks' +import { ArbitrumNetwork } from '../dataEntities/networks' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { FetchedEvent, EventFetcher } from '../utils/eventFetcher' import { MultiCaller, CallInput } from '../utils/multicall' @@ -66,19 +62,14 @@ export class InboxTools { * Parent chain provider */ private readonly parentChainProvider: Provider - /** - * Parent chain for the given Arbitrum chain, can be an L1 or an L2 - */ - private readonly parentChain: ParentChain | ChildChain constructor( private readonly parentChainSigner: Signer, - private readonly childChain: ChildChain + private readonly childChain: ArbitrumNetwork ) { this.parentChainProvider = SignerProviderUtils.getProviderOrThrow( this.parentChainSigner ) - this.parentChain = getParentForNetwork(childChain) } /** @@ -99,10 +90,10 @@ export class InboxTools { // we take a long average block time of 14s // and always move at least 10 blocks - const diffBlocks = Math.max( - Math.ceil(diff / this.parentChain.blockTime), - 10 - ) + + // todo(spsjvc): do something about this + const blockTime = 12 + const diffBlocks = Math.max(Math.ceil(diff / blockTime), 10) return await this.findFirstBlockBelow( blockNumber - diffBlocks, diff --git a/src/lib/message/ChildToParentMessage.ts b/src/lib/message/ChildToParentMessage.ts index 59c9ae5c4a..aefe70f1e4 100644 --- a/src/lib/message/ChildToParentMessage.ts +++ b/src/lib/message/ChildToParentMessage.ts @@ -35,7 +35,10 @@ import { import { isDefined } from '../utils/lib' import { EventArgs } from '../dataEntities/event' import { ChildToParentMessageStatus } from '../dataEntities/message' -import { getChildChain, getNitroGenesisBlock } from '../dataEntities/networks' +import { + getArbitrumNetwork, + getNitroGenesisBlock, +} from '../dataEntities/networks' import { ArbSdkError } from '../dataEntities/errors' export type ChildToParentTransactionEvent = @@ -111,7 +114,7 @@ export class ChildToParentMessage { hash?: BigNumber, indexInBatch?: BigNumber ): Promise<(ChildToParentTransactionEvent & { transactionHash: string })[]> { - const childChain = await getChildChain(childChainProvider) + const childChain = await getArbitrumNetwork(childChainProvider) const childChainNitroGenesisBlock = getNitroGenesisBlock(childChain) const inClassicRange = (blockTag: BlockTag, nitroGenBlock: number) => { diff --git a/src/lib/message/ChildToParentMessageClassic.ts b/src/lib/message/ChildToParentMessageClassic.ts index d023afeef4..e646083a7d 100644 --- a/src/lib/message/ChildToParentMessageClassic.ts +++ b/src/lib/message/ChildToParentMessageClassic.ts @@ -40,7 +40,7 @@ import { isDefined, wait } from '../utils/lib' import { ArbSdkError } from '../dataEntities/errors' import { EventArgs } from '../dataEntities/event' import { ChildToParentMessageStatus } from '../dataEntities/message' -import { getChildChain } from '../dataEntities/networks' +import { getArbitrumNetwork } from '../dataEntities/networks' export interface MessageBatchProofInfo { /** @@ -213,7 +213,7 @@ export class ChildToParentMessageReaderClassic extends ChildToParentMessageClass batchNumber: number ) { if (!isDefined(this.outboxAddress)) { - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) // find the outbox where the activation batch number of the next outbox // is greater than the supplied batch diff --git a/src/lib/message/ChildToParentMessageNitro.ts b/src/lib/message/ChildToParentMessageNitro.ts index 8c9cb6f102..7bdc98f2ad 100644 --- a/src/lib/message/ChildToParentMessageNitro.ts +++ b/src/lib/message/ChildToParentMessageNitro.ts @@ -40,7 +40,7 @@ import { SignerOrProvider, } from '../dataEntities/signerOrProvider' import { getBlockRangesForL1Block, isArbitrumChain, wait } from '../utils/lib' -import { getChildChain } from '../dataEntities/networks' +import { getArbitrumNetwork } from '../dataEntities/networks' import { NodeCreatedEvent, RollupUserLogic } from '../abi/RollupUserLogic' import { ArbitrumProvider } from '../utils/arbProvider' import { ArbBlock } from '../dataEntities/rpc' @@ -222,7 +222,7 @@ export class ChildToParentChainMessageReaderNitro extends ChildToParentChainMess * Check if this message has already been executed in the Outbox */ protected async hasExecuted(childProvider: Provider): Promise { - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) const outbox = Outbox__factory.connect( childChain.ethBridge.outbox, this.parentProvider @@ -375,7 +375,7 @@ export class ChildToParentChainMessageReaderNitro extends ChildToParentChainMess protected async getSendProps(childProvider: Provider) { if (!this.sendRootConfirmed) { - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) const rollup = RollupUserLogic__factory.connect( childChain.ethBridge.rollup, @@ -457,7 +457,7 @@ export class ChildToParentChainMessageReaderNitro extends ChildToParentChainMess public async getFirstExecutableBlock( childProvider: Provider ): Promise { - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) const rollup = RollupUserLogic__factory.connect( childChain.ethBridge.rollup, @@ -574,7 +574,7 @@ export class ChildToParentChainMessageWriterNitro extends ChildToParentChainMess ) } const proof = await this.getOutboxProof(childProvider) - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) const outbox = Outbox__factory.connect( childChain.ethBridge.outbox, this.parentSigner diff --git a/src/lib/message/ParentToChildMessage.ts b/src/lib/message/ParentToChildMessage.ts index a24280385d..dace3146a5 100644 --- a/src/lib/message/ParentToChildMessage.ts +++ b/src/lib/message/ParentToChildMessage.ts @@ -44,7 +44,7 @@ import { import { ArbSdkError } from '../dataEntities/errors' import { Overrides } from 'ethers' import { ChildTransactionReceipt, RedeemTransaction } from './ChildTransaction' -import { getChildChain } from '../../lib/dataEntities/networks' +import { getArbitrumNetwork } from '../../lib/dataEntities/networks' import { RetryableMessageParams } from '../dataEntities/message' import { getTransactionReceipt, isDefined } from '../utils/lib' import { EventFetcher } from '../utils/eventFetcher' @@ -323,7 +323,7 @@ export class ParentToChildMessageReader extends ParentToChildMessage { * @returns TransactionReceipt of the first successful redeem if exists, otherwise the current status of the message. */ public async getSuccessfulRedeem(): Promise { - const chainNetwork = await getChildChain(this.chainProvider) + const chainNetwork = await getArbitrumNetwork(this.chainProvider) const eventFetcher = new EventFetcher(this.chainProvider) const creationReceipt = await this.getRetryableCreationReceipt() diff --git a/src/lib/message/ParentToChildMessageCreator.ts b/src/lib/message/ParentToChildMessageCreator.ts index e06a0105ce..5e83c164ef 100644 --- a/src/lib/message/ParentToChildMessageCreator.ts +++ b/src/lib/message/ParentToChildMessageCreator.ts @@ -11,7 +11,7 @@ import { ParentTransactionReceipt, } from './ParentTransaction' import { Inbox__factory } from '../abi/factories/Inbox__factory' -import { getChildChain } from '../dataEntities/networks' +import { getArbitrumNetwork } from '../dataEntities/networks' import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' import { PayableOverrides } from '@ethersproject/contracts' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' @@ -155,7 +155,7 @@ export class ParentToChildMessageCreator { options ) - const childChain = await getChildChain(childProvider) + const childChain = await getArbitrumNetwork(childProvider) const nativeTokenIsEth = typeof childChain.nativeToken === 'undefined' const data = ParentToChildMessageCreator.getTicketCreationRequestCallData( diff --git a/src/lib/message/ParentToChildMessageGasEstimator.ts b/src/lib/message/ParentToChildMessageGasEstimator.ts index a4b5098d34..f8ae584ecf 100644 --- a/src/lib/message/ParentToChildMessageGasEstimator.ts +++ b/src/lib/message/ParentToChildMessageGasEstimator.ts @@ -1,11 +1,12 @@ import { Provider } from '@ethersproject/abstract-provider' import { BigNumber, BigNumberish } from '@ethersproject/bignumber' -import { BytesLike, constants, utils } from 'ethers' +import { BytesLike, constants } from 'ethers' +import { parseEther, dataLength } from 'ethers-v6' import { Inbox__factory } from '../abi/factories/Inbox__factory' import { NodeInterface__factory } from '../abi/factories/NodeInterface__factory' import { NODE_INTERFACE_ADDRESS } from '../dataEntities/constants' import { ArbSdkError } from '../dataEntities/errors' -import { getChildChain } from '../dataEntities/networks' +import { getArbitrumNetwork } from '../dataEntities/networks' import { RetryableData, RetryableDataTools, @@ -125,7 +126,7 @@ export class ParentToChildMessageGasEstimator { ): Promise { const defaultedOptions = this.applySubmissionPriceDefaults(options) - const network = await getChildChain(this.childProvider) + const network = await getArbitrumNetwork(this.childProvider) const inbox = Inbox__factory.connect( network.ethBridge.inbox, parentProvider @@ -157,7 +158,7 @@ export class ParentToChildMessageGasEstimator { callValueRefundAddress, data, }: ParentToChildMessageNoGasParams, - senderDeposit: BigNumber = utils.parseEther('1').add(l2CallValue) + senderDeposit: BigNumber = BigNumber.from(parseEther('1')).add(l2CallValue) ): Promise { const nodeInterface = NodeInterface__factory.connect( NODE_INTERFACE_ADDRESS, @@ -233,7 +234,7 @@ export class ParentToChildMessageGasEstimator { const maxSubmissionFeePromise = this.estimateSubmissionFee( parentProvider, parentBaseFee, - utils.hexDataLength(data), + dataLength(data), options?.maxSubmissionFee ) diff --git a/src/lib/message/ParentTransaction.ts b/src/lib/message/ParentTransaction.ts index 7c82c1ff12..2d1a864aff 100644 --- a/src/lib/message/ParentTransaction.ts +++ b/src/lib/message/ParentTransaction.ts @@ -112,7 +112,7 @@ export class ParentTransactionReceipt implements TransactionReceipt { const network = await getArbitrumNetwork(provider) // all networks except Arbitrum One started off with Nitro - if (network.chainID === 42161) { + if (network.chainId === 42161) { return this.blockNumber < ARB1_NITRO_GENESIS_L1_BLOCK } @@ -217,7 +217,7 @@ export class ParentTransactionReceipt implements TransactionReceipt { childProvider: Provider ): Promise { const network = await getArbitrumNetwork(childProvider) - const chainID = network.chainID.toString() + const chainId = network.chainId.toString() const isClassic = await this.isClassic(childProvider) // throw on nitro events @@ -235,7 +235,7 @@ export class ParentTransactionReceipt implements TransactionReceipt { messageNum => new ParentToChildMessageReaderClassic( childProvider, - BigNumber.from(chainID).toNumber(), + BigNumber.from(chainId).toNumber(), messageNum ) ) @@ -255,7 +255,7 @@ export class ParentTransactionReceipt implements TransactionReceipt { childSignerOrProvider ) const network = await getArbitrumNetwork(provider) - const chainID = network.chainID.toString() + const chainId = network.chainId.toString() const isClassic = await this.isClassic(provider) // throw on classic events @@ -282,7 +282,7 @@ export class ParentTransactionReceipt implements TransactionReceipt { return ParentToChildMessage.fromEventComponents( childSignerOrProvider, - BigNumber.from(chainID).toNumber(), + BigNumber.from(chainId).toNumber(), mn.bridgeMessageEvent.sender, mn.inboxMessageEvent.messageNum, mn.bridgeMessageEvent.baseFeeL1, diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 9d5d20c907..1e93f623ea 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -17,19 +17,13 @@ 'use strict' import { Provider } from '@ethersproject/abstract-provider' -import { BigNumber, utils } from 'ethers' +import { BigNumber } from 'ethers' +import { isHexString, decodeBytes32String, dataLength } from 'ethers-v6' import { ERC20__factory } from '../abi/factories/ERC20__factory' import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' -import { ArbSdkError } from '../dataEntities/errors' -import { - isL1Network, - L1Network, - l1Networks, - L2Network, - l2Networks, -} from '../dataEntities/networks' +import { getMulticallAddress } from '../dataEntities/networks' /** * Input to multicall aggregator @@ -130,33 +124,7 @@ export class MultiCaller { * @returns */ public static async fromProvider(provider: Provider): Promise { - const chainId = (await provider.getNetwork()).chainId - const l2Network = l2Networks[chainId] as L2Network | undefined - const l1Network = l1Networks[chainId] as L1Network | undefined - - const network = l2Network || l1Network - if (!network) { - throw new ArbSdkError( - `Unexpected network id: ${chainId}. Ensure that chain ${chainId} has been added as a network.` - ) - } - - let multiCallAddr: string - if (isL1Network(network)) { - // If the network is an L1, find one of its L2s and pick up the multicall address from there - const firstL2 = [...Object.values(l2Networks)].find( - chain => chain.parentChainId === network.chainID - ) - if (!firstL2) - throw new ArbSdkError( - `No children chains found for network: ${network.chainID}` - ) - multiCallAddr = firstL2.tokenBridge.l1MultiCall - } else { - multiCallAddr = network.tokenBridge.l2Multicall - } - - return new MultiCaller(provider, multiCallAddr) + return new MultiCaller(provider, await getMulticallAddress(provider)) } /** @@ -284,7 +252,7 @@ export class MultiCaller { const erc20Iface = ERC20__factory.createInterface() const isBytes32 = (data: string) => - utils.isHexString(data) && utils.hexDataLength(data) === 32 + isHexString(data) && dataLength(data) === 32 const input = [] for (const t of erc20Addresses) { @@ -339,7 +307,7 @@ export class MultiCaller { // Maker doesn't follow the erc20 spec and returns bytes32 data. // https://etherscan.io/token/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2#readContract if (isBytes32(returnData)) { - return utils.parseBytes32String(returnData) as string + return decodeBytes32String(returnData) as string } else return erc20Iface.decodeFunctionResult( 'name', @@ -357,7 +325,7 @@ export class MultiCaller { // Maker doesn't follow the erc20 spec and returns bytes32 data. // https://etherscan.io/token/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2#readContract if (isBytes32(returnData)) { - return utils.parseBytes32String(returnData) as string + return decodeBytes32String(returnData) as string } else return erc20Iface.decodeFunctionResult( 'symbol', diff --git a/src/lib/utils/types.ts b/src/lib/utils/types.ts index dc0707ea5b..48d60a6a2e 100644 --- a/src/lib/utils/types.ts +++ b/src/lib/utils/types.ts @@ -14,3 +14,9 @@ export type PartialPick = OmitTyped & Partial * Make the specified properties required */ export type RequiredPick = Required> & T + +// https://twitter.com/mattpocockuk/status/1622730173446557697 +export type Prettify = { + [K in keyof T]: T[K] + // eslint-disable-next-line @typescript-eslint/ban-types +} & {} diff --git a/tests/fork/inbox.test.ts b/tests/fork/inbox.test.ts index 7362c01143..85e58893e2 100644 --- a/tests/fork/inbox.test.ts +++ b/tests/fork/inbox.test.ts @@ -29,13 +29,12 @@ import { SequencerInbox__factory } from '../../src/lib/abi/factories/SequencerIn import { InboxTools } from '../../src' import { ethers, network } from 'hardhat' -import { zeroPadValue } from 'ethers-v6' +import { ContractTransaction, Signer } from 'ethers' +import { zeroPadValue, solidityPackedKeccak256 } from 'ethers-v6' import { - ChildChain as L2Network, - getChildChain as getL2Network, + ArbitrumNetwork, + getArbitrumNetwork, } from '../../src/lib/dataEntities/networks' -import { solidityKeccak256 } from 'ethers/lib/utils' -import { ContractTransaction, Signer } from 'ethers' const submitL2Tx = async ( tx: { @@ -46,7 +45,7 @@ const submitL2Tx = async ( maxFeePerGas: BigNumber gasLimit: BigNumber }, - l2Network: L2Network, + l2Network: ArbitrumNetwork, l1Signer: Signer ): Promise => { const inbox = Inbox__factory.connect(l2Network.ethBridge.inbox, l1Signer) @@ -67,7 +66,7 @@ describe('Inbox tools', () => { const signer = signers[0] const provider = signer.provider! - const arbitrumOne = await getL2Network(42161) + const arbitrumOne = await getArbitrumNetwork(42161) const sequencerInbox = SequencerInbox__factory.connect( arbitrumOne.ethBridge.sequencerInbox, @@ -210,7 +209,7 @@ describe('Inbox tools', () => { maxFeePerGas: BigNumber.from(21000000000), nonce: 1, } - const messageDataHash = solidityKeccak256( + const messageDataHash = solidityPackedKeccak256( ['uint8', 'uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes'], [ 0, diff --git a/tests/integration/childTransactionReceipt.test.ts b/tests/integration/childTransactionReceipt.test.ts index b5fc17372e..2f1490d8b5 100644 --- a/tests/integration/childTransactionReceipt.test.ts +++ b/tests/integration/childTransactionReceipt.test.ts @@ -28,7 +28,7 @@ import { import { ChildTransactionReceipt } from '../../src' import { JsonRpcProvider } from '@ethersproject/providers' import { BigNumber, Wallet } from 'ethers' -import { parseEther } from 'ethers/lib/utils' +import { parseEther } from 'ethers-v6' import { testSetup } from '../../scripts/testSetup' describe('ArbProvider', () => { @@ -43,8 +43,8 @@ describe('ArbProvider', () => { // set up miners const miner1 = Wallet.createRandom().connect(parentSigner.provider!) const miner2 = Wallet.createRandom().connect(childSigner.provider!) - await fundParentSigner(miner1, parseEther('0.1')) - await fundChildSigner(miner2, parseEther('0.1')) + await fundParentSigner(miner1, BigNumber.from(parseEther('0.1'))) + await fundChildSigner(miner2, BigNumber.from(parseEther('0.1'))) const state = { mining: true } mineUntilStop(miner1, state) mineUntilStop(miner2, state) diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index ae8e816120..2995fd3ea5 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -1,6 +1,6 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' -import { Signer, Wallet, utils } from 'ethers' -import { ZeroAddress } from 'ethers-v6' +import { Signer, Wallet } from 'ethers' +import { ZeroAddress, parseEther, sha256, toUtf8Bytes } from 'ethers-v6' import { testSetup as _testSetup, @@ -49,13 +49,13 @@ export async function fundParentCustomFeeToken( } const deployerWallet = new Wallet( - utils.sha256(utils.toUtf8Bytes('user_token_bridge_deployer')), + sha256(toUtf8Bytes('user_token_bridge_deployer')), ethProvider() ) const tokenContract = ERC20__factory.connect(nativeToken, deployerWallet) - const tx = await tokenContract.transfer(address, utils.parseEther('10')) + const tx = await tokenContract.transfer(address, parseEther('10')) await tx.wait() } @@ -96,7 +96,7 @@ export async function fundChildCustomFeeToken(childSigner: Signer) { const tx = await deployerWallet.sendTransaction({ to: await childSigner.getAddress(), - value: utils.parseEther('1'), + value: parseEther('1'), }) await tx.wait() } diff --git a/tests/integration/customerc20.test.ts b/tests/integration/customerc20.test.ts index a7a0fd0625..5e14d1e79b 100644 --- a/tests/integration/customerc20.test.ts +++ b/tests/integration/customerc20.test.ts @@ -17,7 +17,7 @@ 'use strict' import { expect } from 'chai' -import { Signer, Wallet, utils } from 'ethers' +import { Signer, Wallet } from 'ethers' import { ZeroAddress, parseEther } from 'ethers-v6' import { BigNumber } from '@ethersproject/bignumber' import { Logger, LogLevel } from '@ethersproject/logger' @@ -123,7 +123,7 @@ describe('Custom ERC20', () => { it('deposits erc20 with extra ETH', async () => { await depositToken({ depositAmount, - ethDepositAmount: utils.parseEther('0.0005'), + ethDepositAmount: BigNumber.from(parseEther('0.0005')), parentTokenAddress: testState.l1CustomToken.address, erc20Bridger: testState.adminErc20Bridger, parentSigner: testState.parentSigner, @@ -137,7 +137,7 @@ describe('Custom ERC20', () => { const randomAddress = Wallet.createRandom().address await depositToken({ depositAmount, - ethDepositAmount: utils.parseEther('0.0005'), + ethDepositAmount: BigNumber.from(parseEther('0.0005')), parentTokenAddress: testState.l1CustomToken.address, erc20Bridger: testState.adminErc20Bridger, parentSigner: testState.parentSigner, diff --git a/tests/integration/ethBridgeAddresses.test.ts b/tests/integration/ethBridgeAddresses.test.ts index 57c9eb8b62..6fa8fc0671 100644 --- a/tests/integration/ethBridgeAddresses.test.ts +++ b/tests/integration/ethBridgeAddresses.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai' import { EthBridge, getEthBridgeInformation, - getChildChain as getL2Network, + getArbitrumNetwork, } from '../../src/lib/dataEntities/networks' import dotenv from 'dotenv' dotenv.config() @@ -14,7 +14,7 @@ dotenv.config() */ describe('Obtain deployed bridge addresses', () => { it('obtains deployed ETH Bridge addresses', async () => { - const arbOneL2Network = await getL2Network(42161) + const arbOneL2Network = await getArbitrumNetwork(42161) const ethProvider = new JsonRpcProvider( process.env['MAINNET_RPC'] as string ) diff --git a/tests/integration/parentToChildMessageCreator.test.ts b/tests/integration/parentToChildMessageCreator.test.ts index 079ea7ea3c..30f3da9898 100644 --- a/tests/integration/parentToChildMessageCreator.test.ts +++ b/tests/integration/parentToChildMessageCreator.test.ts @@ -17,7 +17,8 @@ 'use strict' import { expect } from 'chai' -import { providers, utils } from 'ethers' +import { BigNumber, providers } from 'ethers' +import { parseEther } from 'ethers-v6' import { fundParentSigner, skipIfMainnet } from './testHelpers' import { testSetup } from '../../scripts/testSetup' import { ParentToChildMessageCreator } from '../../src/lib/message/ParentToChildMessageCreator' @@ -35,7 +36,7 @@ describe('ParentToChildMessageCreator', () => { }) // Testing amount - const testAmount = utils.parseEther('0.01') + const testAmount = BigNumber.from(parseEther('0.01')) it('allows the creation of Retryable Tickets sending parameters', async () => { const { parentSigner, childSigner } = await testSetup() diff --git a/tests/integration/retryableData.test.ts b/tests/integration/retryableData.test.ts index a81768dccf..9db5c920c9 100644 --- a/tests/integration/retryableData.test.ts +++ b/tests/integration/retryableData.test.ts @@ -18,13 +18,12 @@ import { assert, expect } from 'chai' import { BigNumber } from '@ethersproject/bignumber' -import { hexlify } from 'ethers-v6' +import { hexlify, parseEther, randomBytes } from 'ethers-v6' import { TestERC20__factory } from '../../src/lib/abi/factories/TestERC20__factory' import { fundParentSigner, skipIfMainnet } from './testHelpers' import { RetryableDataTools } from '../../src' import { Wallet } from 'ethers' import { testSetup } from '../../scripts/testSetup' -import { parseEther, randomBytes } from 'ethers/lib/utils' import { Inbox__factory } from '../../src/lib/abi/factories/Inbox__factory' import { GasOverrides } from '../../src/lib/message/ParentToChildMessageGasEstimator' const depositAmount = BigNumber.from(100) @@ -146,7 +145,7 @@ describe('RevertData', () => { it('is the same as what we estimate in erc20Bridger', async () => { const { erc20Bridger, parentSigner, childSigner } = await testSetup() - await fundParentSigner(parentSigner, parseEther('2')) + await fundParentSigner(parentSigner, BigNumber.from(parseEther('2'))) const deployErc20 = new TestERC20__factory().connect(parentSigner) const testToken = await deployErc20.deploy() diff --git a/tests/integration/sanity.test.ts b/tests/integration/sanity.test.ts index 81e6b9e138..95e081b6f5 100644 --- a/tests/integration/sanity.test.ts +++ b/tests/integration/sanity.test.ts @@ -28,7 +28,7 @@ import { L2ERC20Gateway__factory } from '../../src/lib/abi/factories/L2ERC20Gate import { L1ERC20Gateway__factory } from '../../src/lib/abi/factories/L1ERC20Gateway__factory' import { testSetup } from '../../scripts/testSetup' -import { randomBytes, hexlify } from 'ethers/lib/utils' +import { randomBytes, hexlify } from 'ethers-v6' import { itOnlyWhenEth } from './custom-fee-token/mochaExtensions' const expectIgnoreCase = (expected: string, actual: string) => { diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index af08fc29ca..489d744e93 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -17,7 +17,7 @@ 'use strict' import { expect } from 'chai' -import { Signer, Wallet, utils, constants } from 'ethers' +import { Signer, Wallet, constants } from 'ethers' import { BigNumber } from '@ethersproject/bignumber' import { TestERC20__factory } from '../../src/lib/abi/factories/TestERC20__factory' import { @@ -51,6 +51,7 @@ import { isArbitrumNetworkWithCustomFeeToken, } from './custom-fee-token/customFeeTokenTestHelpers' import { itOnlyWhenCustomGasToken } from './custom-fee-token/mochaExtensions' +import { parseEther } from 'ethers-v6' const depositAmount = BigNumber.from(100) const withdrawalAmount = BigNumber.from(10) @@ -302,7 +303,7 @@ describe('standard ERC20', () => { it('deposits erc20 with extra ETH', async () => { await depositToken({ depositAmount, - ethDepositAmount: utils.parseEther('0.0005'), + ethDepositAmount: BigNumber.from(parseEther('0.0005')), parentTokenAddress: testState.parentToken.address, erc20Bridger: testState.erc20Bridger, parentSigner: testState.parentSigner, @@ -316,7 +317,7 @@ describe('standard ERC20', () => { const randomAddress = Wallet.createRandom().address await depositToken({ depositAmount, - ethDepositAmount: utils.parseEther('0.0005'), + ethDepositAmount: BigNumber.from(parseEther('0.0005')), parentTokenAddress: testState.parentToken.address, erc20Bridger: testState.erc20Bridger, parentSigner: testState.parentSigner, diff --git a/tests/integration/testHelpers.ts b/tests/integration/testHelpers.ts index a2a1c6b5b6..7ad2ece9a9 100644 --- a/tests/integration/testHelpers.ts +++ b/tests/integration/testHelpers.ts @@ -443,10 +443,10 @@ export const skipIfMainnet = (() => { let chainId: number return async (testContext: Mocha.Context) => { if (!chainId) { - const { parentChain } = await testSetup() - chainId = parentChain.chainID + const { childChain } = await testSetup() + chainId = childChain.chainId } - if (chainId === 1) { + if (chainId === 42161 || chainId === 42170) { console.error("You're writing to the chain on mainnet lol stop") testContext.skip() } diff --git a/tests/unit/childToParentMessageEvents.test.ts b/tests/unit/childToParentMessageEvents.test.ts index 0d505da4c9..0e5d3f7316 100644 --- a/tests/unit/childToParentMessageEvents.test.ts +++ b/tests/unit/childToParentMessageEvents.test.ts @@ -43,7 +43,7 @@ describe('ChildToParentMessage events', () => { const latestBlock = getNitroGenesisBlock(l2Network) + 1000 when(l2ProviderMock.getBlockNumber()).thenResolve(latestBlock) when(l2ProviderMock.getNetwork()).thenResolve({ - chainId: l2Network.chainID, + chainId: l2Network.chainId, } as any) when(l2ProviderMock._isProvider).thenReturn(true) when(l2ProviderMock.getLogs(anything())).thenResolve([]) diff --git a/tests/unit/messageDataParser.test.ts b/tests/unit/messageDataParser.test.ts index e6b359f59c..e7ba0a9658 100644 --- a/tests/unit/messageDataParser.test.ts +++ b/tests/unit/messageDataParser.test.ts @@ -4,7 +4,7 @@ import { expect } from 'chai' import { BigNumber } from 'ethers' -import { parseEther } from 'ethers/lib/utils' +import { parseEther } from 'ethers-v6' import { SubmitRetryableMessageDataParser } from '../../src/lib/message/messageDataParser' describe('SubmitRetryableMessageDataParser', () => { diff --git a/tests/unit/multicall.test.ts b/tests/unit/multicall.test.ts index cef88f399c..04e30a3b3b 100644 --- a/tests/unit/multicall.test.ts +++ b/tests/unit/multicall.test.ts @@ -1,7 +1,7 @@ 'use strict' import { - getChildChain as getL2Network, + getArbitrumNetwork, getNitroGenesisBlock, } from '../../src/lib/dataEntities/networks' import { providers } from 'ethers' @@ -12,18 +12,18 @@ import { MultiCaller } from '../../src' describe('Multicall', () => { const createProviderMock = async (networkChoiceOverride?: number) => { - const l2Network = await getL2Network(networkChoiceOverride || 42161) + const l2Network = await getArbitrumNetwork(networkChoiceOverride || 42161) const l2ProviderMock = mock(providers.JsonRpcProvider) const latestBlock = getNitroGenesisBlock(l2Network) + 1000 when(l2ProviderMock.getBlockNumber()).thenResolve(latestBlock) when(l2ProviderMock.getNetwork()).thenResolve({ - chainId: l2Network.chainID, + chainId: l2Network.chainId, } as any) when(l2ProviderMock._isProvider).thenReturn(true) when(l2ProviderMock.getLogs(anything())).thenResolve([]) - /* + /* This test data is taken from an actual example of a mainnet multicall. To produce this data we do the following: 1. Pass mainnet args to the multicall class, instantiated with a mock provider 2. Capture the .call request that was made on the provider diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index dac3faf2e4..75d41559d4 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -1,19 +1,17 @@ import { expect } from 'chai' import { resetNetworksToDefault, - addCustomNetwork, - getL1Network, + addCustomArbitrumNetwork, getArbitrumNetwork, - l1Networks, l2Networks as arbitrumNetworks, getChildrenForNetwork, - isParentChain, + isParentNetwork, + getMulticallAddress, } from '../../src/lib/dataEntities/networks' const ethereumMainnetChainId = 1 const arbitrumOneChainId = 42161 -const mockL1ChainId = 111111 const mockL2ChainId = 222222 const mockL3ChainId = 99999999 @@ -23,71 +21,40 @@ describe('Networks', async () => { }) describe('adding networks', () => { - it('adds a custom Arbitrum network', async function () { + it('adds a custom L2 network', async function () { const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) const customArbitrumNetwork = { ...arbitrumOne, - chainID: mockL2ChainId, + chainId: mockL2ChainId, parentChainId: ethereumMainnetChainId, isArbitrum: true, isCustom: true, } as const - addCustomNetwork({ customArbitrumNetwork }) + addCustomArbitrumNetwork(customArbitrumNetwork) expect(await getArbitrumNetwork(mockL2ChainId)).to.be.ok // assert network has correct parent const arbitrumNetwork = await getArbitrumNetwork( - customArbitrumNetwork.chainID + customArbitrumNetwork.chainId ) expect(arbitrumNetwork.parentChainId).to.equal(ethereumMainnetChainId) }) - it('adds a custom L1 and Arbitrum network', async function () { - const ethereumMainnet = await getL1Network(ethereumMainnetChainId) - const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) - - const customL1Network = { - ...ethereumMainnet, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - } as const - - const customArbitrumNetwork = { - ...arbitrumOne, - parentChainId: mockL1ChainId, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - } as const - - addCustomNetwork({ customL1Network, customArbitrumNetwork }) - - expect(await getL1Network(mockL1ChainId)).to.be.ok - expect(await getArbitrumNetwork(mockL2ChainId)).to.be.ok - - // assert network has correct parent - const arbitrumNetwork = await getArbitrumNetwork( - customArbitrumNetwork.chainID - ) - expect(arbitrumNetwork.parentChainId).to.equal(mockL1ChainId) - }) - it('adds a custom L3 network', async function () { const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) const customArbitrumNetwork = { ...arbitrumOne, - chainID: mockL3ChainId, + chainId: mockL3ChainId, parentChainId: arbitrumOneChainId, isArbitrum: true, isCustom: true, } as const - addCustomNetwork({ customArbitrumNetwork }) + addCustomArbitrumNetwork(customArbitrumNetwork) expect(await getArbitrumNetwork(mockL3ChainId)).to.be.ok @@ -95,128 +62,12 @@ describe('Networks', async () => { const l3Network = await getArbitrumNetwork(mockL3ChainId) expect(l3Network.parentChainId).to.equal(arbitrumOneChainId) }) - - it('adds a custom L1, L2, and L3 network', async function () { - const ethereumMainnet = await getL1Network(ethereumMainnetChainId) - const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) - - const customL1Network = { - ...ethereumMainnet, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - } as const - - const customArbitrumNetwork = { - ...arbitrumOne, - chainID: mockL2ChainId, - parentChainId: mockL1ChainId, - isArbitrum: true, - isCustom: true, - } as const - - addCustomNetwork({ customL1Network, customArbitrumNetwork }) - - expect(await getL1Network(mockL1ChainId)).to.be.ok - expect(await getArbitrumNetwork(mockL2ChainId)).to.be.ok - - // assert network has correct parent - const arbitrumNetwork = await getArbitrumNetwork(mockL2ChainId) - expect(arbitrumNetwork.parentChainId).to.equal(mockL1ChainId) - - const customL3Network = { - ...arbitrumOne, - chainID: mockL3ChainId, - parentChainId: mockL2ChainId, - isArbitrum: true, - isCustom: true, - } as const - - addCustomNetwork({ customArbitrumNetwork: customL3Network }) - - expect(await getArbitrumNetwork(mockL3ChainId)).to.be.ok - - // assert network has correct parent - const l3Network = await getArbitrumNetwork(mockL3ChainId) - expect(l3Network.parentChainId).to.equal(mockL2ChainId) - }) - - it('fails to add a custom L1 and Arbitrum network if they do not match', async function () { - const ethereumMainnet = await getL1Network(ethereumMainnetChainId) - const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) - - const wrongParentChainId = 1241244 - - const customL1Network = { - ...ethereumMainnet, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - } as const - - const customArbitrumNetwork = { - ...arbitrumOne, - parentChainId: wrongParentChainId, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - } as const - - try { - addCustomNetwork({ customL1Network, customArbitrumNetwork }) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Partner chain id for Arbitrum network ${customArbitrumNetwork.chainID} doesn't match the provided L1 network. Expected ${customL1Network.chainID} but got ${wrongParentChainId}.` - ) - } - }) - - it('fails to add a custom L3 without previously registering L2', async function () { - const arbitrumOne = await getArbitrumNetwork(arbitrumOneChainId) - - try { - addCustomNetwork({ - customArbitrumNetwork: { - ...arbitrumOne, - chainID: mockL3ChainId, - parentChainId: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, - }) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Network ${mockL3ChainId}'s parent network ${mockL2ChainId} is not recognized` - ) - } - }) }) describe('fetching networks', () => { - it('successfully fetches an L1 network with `getL1Network`', async function () { - const network = await getL1Network(ethereumMainnetChainId) - expect(network.chainID).to.be.eq(ethereumMainnetChainId) - }) - it('successfully fetches an Arbitrum network with `getArbitrumNetwork`', async function () { const network = await getArbitrumNetwork(arbitrumOneChainId) - expect(network.chainID).to.be.eq(arbitrumOneChainId) - }) - - it('fails to fetch a registered Arbitrum network with `getL1Network`', async function () { - try { - await getL1Network(arbitrumOneChainId) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbitrumOneChainId}.` - ) - } + expect(network.chainId).to.be.eq(arbitrumOneChainId) }) it('fails to fetch a registered L1 network with `getArbitrumNetwork`', async function () { @@ -236,33 +87,20 @@ describe('Networks', async () => { const customL3Network = { ...arbitrumOne, - chainID: mockL3ChainId, + chainId: mockL3ChainId, parentChainId: arbitrumOneChainId, isArbitrum: true, isCustom: true, } as const - addCustomNetwork({ customArbitrumNetwork: customL3Network }) + addCustomArbitrumNetwork(customL3Network) const l3Network = await getArbitrumNetwork(mockL3ChainId) - expect(l3Network.chainID).to.be.eq(mockL3ChainId) + expect(l3Network.chainId).to.be.eq(mockL3ChainId) // assert network has correct parent expect(l3Network.parentChainId).to.equal(arbitrumOneChainId) }) - it('fails to fetch an unrecognized L1 network', async () => { - const chainId = 9999 - - try { - await getL1Network(chainId) - } catch (err) { - expect(err).to.be.instanceOf(Error) - expect((err as Error).message).to.be.eq( - `Unrecognized network ${chainId}.` - ) - } - }) - it('fails to fetch an unrecognized L2/L3 network', async () => { const chainId = 9999 @@ -278,17 +116,6 @@ describe('Networks', async () => { }) describe('returns correct networks', () => { - // todo: this could be a snapshot test - it('returns correct L1 networks', () => { - const l1NetworksEntries = Object.entries(l1Networks) - const l1NetworksKeys = l1NetworksEntries.map(([key]) => key) - - const expected = [1, 1338, 17000, 11155111].map(id => id.toString()) - - expect(l1NetworksKeys).to.have.length(expected.length) - expect(l1NetworksKeys).to.have.members(expected) - }) - // todo: this could be a snapshot test it('returns correct Arbitrum networks', () => { const arbitrumNetworksEntries = Object.entries(arbitrumNetworks) @@ -305,50 +132,77 @@ describe('Networks', async () => { describe('getChildrenForNetwork', () => { it('returns correct children for ethereum mainnet', () => { - const children = getChildrenForNetwork(1).map(c => c.chainID) + const children = getChildrenForNetwork(1).map(c => c.chainId) expect(children).to.have.members([42161, 42170]) }) it('returns correct children for arbitrum one', () => { - const children = getChildrenForNetwork(42161).map(c => c.chainID) + const children = getChildrenForNetwork(42161).map(c => c.chainId) expect(children).to.have.members([]) }) it('returns correct children for arbitrum nova', () => { - const children = getChildrenForNetwork(42170).map(c => c.chainID) + const children = getChildrenForNetwork(42170).map(c => c.chainId) expect(children).to.have.members([]) }) it('returns correct children for sepolia', () => { - const children = getChildrenForNetwork(11155111).map(c => c.chainID) + const children = getChildrenForNetwork(11155111).map(c => c.chainId) expect(children).to.have.members([421614]) }) it('returns correct children for arbitrum sepolia', () => { - const children = getChildrenForNetwork(421614).map(c => c.chainID) + const children = getChildrenForNetwork(421614).map(c => c.chainId) expect(children).to.have.members([23011913]) }) }) - describe('isParentChain', () => { + describe('isParentNetwork', () => { it('returns correct value for ethereum mainnet', () => { - expect(isParentChain(1)).to.equal(true) + expect(isParentNetwork(1)).to.equal(true) }) it('returns correct value for arbitrum one', () => { - expect(isParentChain(42161)).to.equal(false) + expect(isParentNetwork(42161)).to.equal(false) }) it('returns correct value for arbitrum nova', () => { - expect(isParentChain(42170)).to.equal(false) + expect(isParentNetwork(42170)).to.equal(false) }) it('returns correct value for sepolia', () => { - expect(isParentChain(11155111)).to.equal(true) + expect(isParentNetwork(11155111)).to.equal(true) }) it('returns correct value for arbitrum sepolia', () => { - expect(isParentChain(421614)).to.equal(true) + expect(isParentNetwork(421614)).to.equal(true) + }) + }) + + describe('getMulticallAddress', () => { + it('returns correct value for ethereum mainnet', async () => { + const multicall = await getMulticallAddress(1) + expect(multicall).to.equal('0x5ba1e12693dc8f9c48aad8770482f4739beed696') + }) + + it('returns correct value for arbitrum one', async () => { + const multicall = await getMulticallAddress(42161) + expect(multicall).to.equal('0x842eC2c7D803033Edf55E478F461FC547Bc54EB2') + }) + + it('returns correct value for arbitrum nova', async () => { + const multicall = await getMulticallAddress(42170) + expect(multicall).to.equal('0x5e1eE626420A354BbC9a95FeA1BAd4492e3bcB86') + }) + + it('returns correct value for sepolia', async () => { + const multicall = await getMulticallAddress(11155111) + expect(multicall).to.equal('0xded9AD2E65F3c4315745dD915Dbe0A4Df61b2320') + }) + + it('returns correct value for arbitrum sepolia', async () => { + const multicall = await getMulticallAddress(421614) + expect(multicall).to.equal('0xA115146782b7143fAdB3065D86eACB54c169d092') }) }) })