diff --git a/packages/arb-token-bridge-ui/.env.local.sample b/packages/arb-token-bridge-ui/.env.local.sample index 9ccb08c10a..9e0d6cfb67 100644 --- a/packages/arb-token-bridge-ui/.env.local.sample +++ b/packages/arb-token-bridge-ui/.env.local.sample @@ -8,8 +8,10 @@ NEXT_PUBLIC_INFURA_KEY_SEPOLIA= # L2 NEXT_PUBLIC_INFURA_KEY_ARBITRUM_ONE= +NEXT_PUBLIC_INFURA_KEY_BASE= # L2 Testnet NEXT_PUBLIC_INFURA_KEY_ARBITRUM_SEPOLIA= +NEXT_PUBLIC_INFURA_KEY_BASE_SEPOLIA= NEXT_PUBLIC_SENTRY_DSN= diff --git a/packages/arb-token-bridge-ui/public/images/BaseWhite.svg b/packages/arb-token-bridge-ui/public/images/BaseWhite.svg new file mode 100644 index 0000000000..72e52ac2f8 --- /dev/null +++ b/packages/arb-token-bridge-ui/public/images/BaseWhite.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/arb-token-bridge-ui/public/images/PolterTestnetLogo.png b/packages/arb-token-bridge-ui/public/images/PolterTestnetLogo.png new file mode 100644 index 0000000000..70ee5c61fb Binary files /dev/null and b/packages/arb-token-bridge-ui/public/images/PolterTestnetLogo.png differ diff --git a/packages/arb-token-bridge-ui/public/images/ghst.png b/packages/arb-token-bridge-ui/public/images/ghst.png new file mode 100644 index 0000000000..b571b7eef9 Binary files /dev/null and b/packages/arb-token-bridge-ui/public/images/ghst.png differ diff --git a/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx b/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx index 95994fa261..522a69e6cb 100644 --- a/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx +++ b/packages/arb-token-bridge-ui/src/components/common/NetworkSelectionContainer.tsx @@ -32,10 +32,11 @@ import { useActions } from '../../state' import { useChainIdsForNetworkSelection } from '../../hooks/TransferPanel/useChainIdsForNetworkSelection' import { useAccountType } from '../../hooks/useAccountType' -type NetworkType = 'core' | 'orbit' +type NetworkType = 'core' | 'other' | 'orbit' enum ChainGroupName { core = 'CORE CHAINS', + other = 'OTHER CHAINS', orbit = 'ORBIT CHAINS' } @@ -48,6 +49,19 @@ const chainGroupInfo: { [key in NetworkType]: ChainGroupInfo } = { core: { name: ChainGroupName.core }, + other: { + name: ChainGroupName.other, + description: ( +

+ + + Independent projects using non-Arbitrum technology. These chains have + varying degrees of decentralization.{' '} + Bridge at your own risk. + +

+ ) + }, orbit: { name: ChainGroupName.orbit, description: ( @@ -71,7 +85,7 @@ function ChainTypeInfoRow({ style: CSSProperties }) { const { name, description } = chainGroup - const isCoreGroup = chainGroup.name === ChainGroupName.core + const isOrbitGroup = chainGroup.name === ChainGroupName.orbit return (
@@ -236,7 +250,11 @@ function NetworksPanel({ } const coreNetworks = chainIds.filter( - chainId => !isNetwork(chainId).isOrbitChain + chainId => isNetwork(chainId).isCoreChain + ) + const otherNetworks = chainIds.filter( + chainId => + !isNetwork(chainId).isCoreChain && !isNetwork(chainId).isOrbitChain ) const orbitNetworks = chainIds.filter( chainId => isNetwork(chainId).isOrbitChain @@ -244,6 +262,7 @@ function NetworksPanel({ return { core: coreNetworks, + other: otherNetworks, orbit: orbitNetworks } }, [debouncedNetworkSearched, chainIds]) @@ -262,6 +281,10 @@ function NetworksPanel({ groupedNetworks.push(ChainGroupName.core, ...networksToShow.core) } + if (networksToShow.other.length > 0) { + groupedNetworks.push(ChainGroupName.other, ...networksToShow.other) + } + if (networksToShow.orbit.length > 0) { groupedNetworks.push(ChainGroupName.orbit, ...networksToShow.orbit) } @@ -302,6 +325,12 @@ function NetworksPanel({ ) } + if (networkOrChainTypeName === ChainGroupName.other) { + return ( + + ) + } + if (networkOrChainTypeName === ChainGroupName.orbit) { return ( diff --git a/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts b/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts index 0e0df4907d..b141f2d144 100644 --- a/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts +++ b/packages/arb-token-bridge-ui/src/hooks/useNetworks.ts @@ -12,7 +12,9 @@ import { arbitrumSepolia, localL1Network as local, localL2Network as arbitrumLocal, - localL3Network as l3Local + localL3Network as l3Local, + base, + baseSepolia } from '../util/wagmi/wagmiAdditionalNetworks' import { getDestinationChainIds } from '../util/networks' @@ -37,7 +39,9 @@ export function isSupportedChainId( holesky.id, arbitrum.id, arbitrumNova.id, + base.id, arbitrumSepolia.id, + baseSepolia.id, arbitrumLocal.id, l3Local.id, local.id, diff --git a/packages/arb-token-bridge-ui/src/token-bridge-sdk/utils.ts b/packages/arb-token-bridge-ui/src/token-bridge-sdk/utils.ts index 7dbf694b29..54e7483bbf 100644 --- a/packages/arb-token-bridge-ui/src/token-bridge-sdk/utils.ts +++ b/packages/arb-token-bridge-ui/src/token-bridge-sdk/utils.ts @@ -11,6 +11,7 @@ import { EthL1L3Bridger, getArbitrumNetwork } from '@arbitrum/sdk' +import { isDepositMode } from '../util/isDepositMode' export const getAddressFromSigner = async (signer: Signer) => { const address = await signer.getAddress() @@ -28,8 +29,6 @@ export const getBridgeTransferProperties = ( const sourceChainId = props.sourceChainId const destinationChainId = props.destinationChainId - const isSourceChainEthereumMainnetOrTestnet = - isNetwork(sourceChainId).isEthereumMainnetOrTestnet const isDestinationChainEthereumMainnetOrTestnet = isNetwork(destinationChainId).isEthereumMainnetOrTestnet @@ -37,16 +36,16 @@ export const getBridgeTransferProperties = ( const isDestinationChainArbitrum = isNetwork(destinationChainId).isArbitrum const isSourceChainOrbit = isNetwork(sourceChainId).isOrbitChain - const isDestinationChainOrbit = isNetwork(destinationChainId).isOrbitChain - const isDeposit = - isSourceChainEthereumMainnetOrTestnet || - (isSourceChainArbitrum && isDestinationChainOrbit) + const { isBase: isDestinationChainBase } = isNetwork(destinationChainId) + + const isDeposit = isDepositMode({ sourceChainId, destinationChainId }) const isWithdrawal = (isSourceChainArbitrum && isDestinationChainEthereumMainnetOrTestnet) || // l2 arbitrum chains to l1 (isSourceChainOrbit && isDestinationChainEthereumMainnetOrTestnet) || // l2 orbit chains to l1 - (isSourceChainOrbit && isDestinationChainArbitrum) // l3 orbit chains to l1 + (isSourceChainOrbit && isDestinationChainArbitrum) || // l3 orbit chains to l1 + (isSourceChainOrbit && isDestinationChainBase) // l3 orbit chain to Base l2 const isTeleport = isValidTeleportChainPair({ sourceChainId, diff --git a/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts b/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts index 99b20e8463..77abdf0b20 100644 --- a/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts +++ b/packages/arb-token-bridge-ui/src/types/ChainQueryParam.ts @@ -16,7 +16,9 @@ const chainQueryParams = [ 'holesky', 'arbitrum-one', 'arbitrum-nova', + 'base', 'arbitrum-sepolia', + 'base-sepolia', 'custom-localhost', 'arbitrum-localhost', 'l3-localhost' @@ -51,6 +53,9 @@ export function getChainQueryParamForChain(chainId: ChainId): ChainQueryParam { case ChainId.ArbitrumNova: return 'arbitrum-nova' + case ChainId.Base: + return 'base' + case ChainId.Holesky: return 'holesky' @@ -60,6 +65,9 @@ export function getChainQueryParamForChain(chainId: ChainId): ChainQueryParam { case ChainId.ArbitrumSepolia: return 'arbitrum-sepolia' + case ChainId.BaseSepolia: + return 'base-sepolia' + case ChainId.Local: return 'custom-localhost' @@ -107,9 +115,15 @@ export function getChainForChainKeyQueryParam( case 'arbitrum-nova': return customChains.arbitrumNova + case 'base': + return customChains.base + case 'arbitrum-sepolia': return customChains.arbitrumSepolia + case 'base-sepolia': + return customChains.baseSepolia + case 'custom-localhost': return customChains.localL1Network diff --git a/packages/arb-token-bridge-ui/src/util/__tests__/networks.test.ts b/packages/arb-token-bridge-ui/src/util/__tests__/networks.test.ts index 554663bf91..2dbeb6f3cb 100644 --- a/packages/arb-token-bridge-ui/src/util/__tests__/networks.test.ts +++ b/packages/arb-token-bridge-ui/src/util/__tests__/networks.test.ts @@ -52,6 +52,15 @@ beforeAll(() => { }) registerCustomArbitrumNetwork(xaiTestnet) + + const polterTestnetChainId = 631571 + const polterTestnet = orbitTestnets[polterTestnetChainId] + + if (!polterTestnet) { + throw new Error(`Could not find Polter Testnet in the Orbit chains list.`) + } + + registerCustomArbitrumNetwork(polterTestnet) }) describe('getBaseChainIdByChainId', () => { @@ -254,4 +263,20 @@ describe('getDestinationChainIds', () => { expect(defaultChainId).toBe(ChainId.Sepolia) expect(isAscending(nonDefaultChainIds)).toBe(true) }) + + it('should return a sorted list for Base Sepolia', () => { + const destinationChainIds = getDestinationChainIds(ChainId.BaseSepolia) + const defaultChainId = destinationChainIds[0] + const nonDefaultChainIds = destinationChainIds.slice(1) + + expect(defaultChainId).toBe(631571) + expect(isAscending(nonDefaultChainIds)).toBe(true) + }) + + // Enable when there are Orbit Chains on Base + it('should not return a list for Base', () => { + const destinationChainIds = getDestinationChainIds(ChainId.Base) + + expect(destinationChainIds).toHaveLength(0) + }) }) diff --git a/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts b/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts index d68e0a1719..d7d4461918 100644 --- a/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts +++ b/packages/arb-token-bridge-ui/src/util/bridgeUiConfig.ts @@ -104,6 +104,25 @@ export function getBridgeUiConfigForChain(chainId: number): BridgeUiConfig { 'AnyTrust protocol. Low fees for high-volume transactions. Secured by a trust-minimized Data Availability Committee (DAC).' } } + case ChainId.Base: + return { + color: '#0052ff', + network: { + name: 'Base', + logo: '/images/BaseWhite.svg', + description: + 'Base is an Optimistic Rollup built by Coinbase with the OP Stack.' + } + } + case ChainId.BaseSepolia: + return { + color: '#0052ff', + network: { + name: 'Base Sepolia', + logo: '/images/BaseWhite.svg', + description: 'Base Sepolia is an Ethereum L2 testnet by Coinbase.' + } + } default: { // added Orbit chains const orbitChain = orbitChains[chainId] diff --git a/packages/arb-token-bridge-ui/src/util/infura.ts b/packages/arb-token-bridge-ui/src/util/infura.ts index 02124bb907..20fa95514d 100644 --- a/packages/arb-token-bridge-ui/src/util/infura.ts +++ b/packages/arb-token-bridge-ui/src/util/infura.ts @@ -61,10 +61,14 @@ export function chainIdToInfuraKey(chainId: ChainId) { return process.env.NEXT_PUBLIC_INFURA_KEY_SEPOLIA || defaultInfuraKey case ChainId.ArbitrumOne: return process.env.NEXT_PUBLIC_INFURA_KEY_ARBITRUM_ONE || defaultInfuraKey + case ChainId.Base: + return process.env.NEXT_PUBLIC_INFURA_KEY_BASE || defaultInfuraKey case ChainId.ArbitrumSepolia: return ( process.env.NEXT_PUBLIC_INFURA_KEY_ARBITRUM_SEPOLIA || defaultInfuraKey ) + case ChainId.BaseSepolia: + return process.env.NEXT_PUBLIC_INFURA_KEY_BASE_SEPOLIA || defaultInfuraKey default: return defaultInfuraKey @@ -81,8 +85,12 @@ export function chainIdToInfuraUrl(chainId: ChainId) { return `https://sepolia.infura.io/v3/${infuraKey}` case ChainId.ArbitrumOne: return `https://arbitrum-mainnet.infura.io/v3/${infuraKey}` + case ChainId.Base: + return `https://base-mainnet.infura.io/v3/${infuraKey}` case ChainId.ArbitrumSepolia: return `https://arbitrum-sepolia.infura.io/v3/${infuraKey}` + case ChainId.BaseSepolia: + return `https://base-sepolia.infura.io/v3/${infuraKey}` default: return undefined } diff --git a/packages/arb-token-bridge-ui/src/util/isDepositMode.ts b/packages/arb-token-bridge-ui/src/util/isDepositMode.ts index 86f49391ed..dd22cceb7c 100644 --- a/packages/arb-token-bridge-ui/src/util/isDepositMode.ts +++ b/packages/arb-token-bridge-ui/src/util/isDepositMode.ts @@ -9,13 +9,16 @@ export function isDepositMode({ }) { const { isEthereumMainnetOrTestnet: isSourceChainEthereum, - isArbitrum: isSourceChainArbitrum + isArbitrum: isSourceChainArbitrum, + isBase: isSourceChainBase } = isNetwork(sourceChainId) const { isOrbitChain: isDestinationChainOrbit } = isNetwork(destinationChainId) const isDepositMode = - isSourceChainEthereum || (isSourceChainArbitrum && isDestinationChainOrbit) + isSourceChainEthereum || + isSourceChainBase || + (isSourceChainArbitrum && isDestinationChainOrbit) return isDepositMode } diff --git a/packages/arb-token-bridge-ui/src/util/networks.ts b/packages/arb-token-bridge-ui/src/util/networks.ts index 06fe5ca182..5dc8e5627c 100644 --- a/packages/arb-token-bridge-ui/src/util/networks.ts +++ b/packages/arb-token-bridge-ui/src/util/networks.ts @@ -20,20 +20,23 @@ export enum ChainId { // L2 ArbitrumOne = 42161, ArbitrumNova = 42170, + Base = 8453, // L2 Testnets ArbitrumSepolia = 421614, ArbitrumLocal = 412346, + BaseSepolia = 84532, // L3 Testnets L3Local = 333333 } -type L1Network = { +/** The network that you reference when calling `block.number` in solidity */ +type BlockNumberReferenceNetwork = { chainId: ChainId blockTime: number isTestnet: boolean } -const l1Networks: { [chainId: number]: L1Network } = { +const l1Networks: { [chainId: number]: BlockNumberReferenceNetwork } = { [ChainId.Ethereum]: { chainId: ChainId.Ethereum, blockTime: 12, @@ -56,12 +59,32 @@ const l1Networks: { [chainId: number]: L1Network } = { } } +const baseNetworks: { [chainId: number]: BlockNumberReferenceNetwork } = { + [ChainId.Base]: { + chainId: ChainId.Base, + blockTime: 2, + isTestnet: false + }, + [ChainId.BaseSepolia]: { + chainId: ChainId.BaseSepolia, + blockTime: 2, + isTestnet: true + } +} + export const getChains = () => { - const chains = [...Object.values(l1Networks), ...getArbitrumNetworks()] + const chains: (BlockNumberReferenceNetwork | ArbitrumNetwork)[] = [ + ...Object.values(l1Networks), + ...Object.values(baseNetworks), + ...getArbitrumNetworks() + ] return chains.filter(chain => { - // exclude L1 chains with no child chains - if (isL1Chain(chain) && getChildrenForNetwork(chain.chainId).length === 0) { + // exclude L1 chains or Base Chains with no child chains + if ( + isBlockNumberReferenceNetwork(chain) && + getChildrenForNetwork(chain.chainId).length === 0 + ) { return false } @@ -86,12 +109,12 @@ export function getBaseChainIdByChainId({ }: { chainId: number }): number { - // the chain provided is an L1 chain, so we can return early - if (isL1Chain({ chainId })) { + // the chain provided is an L1 chain or Base chain, so we can return early + if (isBlockNumberReferenceNetwork({ chainId })) { return chainId } - let currentParentChain: L1Network | ArbitrumNetwork + let currentParentChain: BlockNumberReferenceNetwork | ArbitrumNetwork try { currentParentChain = getArbitrumNetwork(chainId) @@ -99,9 +122,9 @@ export function getBaseChainIdByChainId({ return chainId } - // keep following the parent chains until we find the L1 chain + // keep following the parent chains until we find the L1/Base chain while (true) { - if (isL1Chain(currentParentChain)) { + if (isBlockNumberReferenceNetwork(currentParentChain)) { return currentParentChain.chainId } @@ -184,10 +207,12 @@ export function removeCustomChainFromLocalStorage(chainId: number) { ) } +// Only support testnet chains export const supportedCustomOrbitParentChains = [ ChainId.Sepolia, ChainId.Holesky, - ChainId.ArbitrumSepolia + ChainId.ArbitrumSepolia, + ChainId.BaseSepolia ] export const rpcURLs: { [chainId: number]: string } = { @@ -208,10 +233,18 @@ export const rpcURLs: { [chainId: number]: string } = { fallback: 'https://arb1.arbitrum.io/rpc' }), [ChainId.ArbitrumNova]: 'https://nova.arbitrum.io/rpc', + [ChainId.Base]: loadEnvironmentVariableWithFallback({ + env: chainIdToInfuraUrl(ChainId.Base), + fallback: 'https://mainnet.base.org' + }), // L2 Testnets [ChainId.ArbitrumSepolia]: loadEnvironmentVariableWithFallback({ env: chainIdToInfuraUrl(ChainId.ArbitrumSepolia), fallback: 'https://sepolia-rollup.arbitrum.io/rpc' + }), + [ChainId.BaseSepolia]: loadEnvironmentVariableWithFallback({ + env: chainIdToInfuraUrl(ChainId.BaseSepolia), + fallback: 'https://sepolia.base.org' }) } @@ -224,8 +257,10 @@ export const explorerUrls: { [chainId: number]: string } = { // L2 [ChainId.ArbitrumNova]: 'https://nova.arbiscan.io', [ChainId.ArbitrumOne]: 'https://arbiscan.io', + [ChainId.Base]: 'https://basescan.org', // L2 Testnets - [ChainId.ArbitrumSepolia]: 'https://sepolia.arbiscan.io' + [ChainId.ArbitrumSepolia]: 'https://sepolia.arbiscan.io', + [ChainId.BaseSepolia]: 'https://sepolia.basescan.org' } export const getExplorerUrl = (chainId: ChainId) => { @@ -236,7 +271,7 @@ export const getExplorerUrl = (chainId: ChainId) => { export const getL1BlockTime = (chainId: number) => { const chain = getChainByChainId(getBaseChainIdByChainId({ chainId })) - if (!chain || !isL1Chain(chain)) { + if (!chain || !isBlockNumberReferenceNetwork(chain)) { throw new Error(`Couldn't get block time. Unexpected chain ID: ${chainId}`) } @@ -244,6 +279,11 @@ export const getL1BlockTime = (chainId: number) => { } export const getConfirmPeriodBlocks = (chainId: ChainId) => { + // Base is not an Arbitrum chain so it doesn't work in the same way, and we don't support deposits from L1, or withdrawals from Base chains + if (isNetwork(chainId).isBase) { + return 0 + } + return getArbitrumNetwork(chainId).confirmPeriodBlocks } @@ -269,7 +309,7 @@ export const l2MoonGatewayAddresses: { [chainId: number]: string } = { [ChainId.ArbitrumNova]: '0xA430a792c14d3E49d9D00FD7B4BA343F516fbB81' } -const defaultL1Network: L1Network = { +const defaultL1Network: BlockNumberReferenceNetwork = { blockTime: 10, chainId: 1337, isTestnet: true @@ -377,6 +417,11 @@ function isTestnetChain(chainId: ChainId) { return l1Network.isTestnet } + const baseNetwork = baseNetworks[chainId] + if (baseNetwork) { + return baseNetwork.isTestnet + } + try { return getArbitrumNetwork(chainId).isTestnet } catch { @@ -405,12 +450,17 @@ export function isNetwork(chainId: ChainId) { const isArbitrumSepolia = chainId === ChainId.ArbitrumSepolia const isArbitrumLocal = chainId === ChainId.ArbitrumLocal + const isBaseMainnet = chainId === ChainId.Base + const isBaseSepolia = chainId === ChainId.BaseSepolia + const isEthereumMainnetOrTestnet = isEthereumMainnet || isSepolia || isHolesky || isLocal const isArbitrum = isArbitrumOne || isArbitrumNova || isArbitrumLocal || isArbitrumSepolia + const isBase = isBaseMainnet || isBaseSepolia + const isCoreChain = isEthereumMainnetOrTestnet || isArbitrum const isOrbitChain = getIsArbitrumChain(chainId) && !isCoreChain @@ -424,8 +474,11 @@ export function isNetwork(chainId: ChainId) { isArbitrum, isArbitrumOne, isArbitrumNova, + isBase, + isBaseMainnet, // L2 Testnets isArbitrumSepolia, + isBaseSepolia, // Orbit chains isOrbitChain, // General @@ -472,22 +525,29 @@ export function mapCustomChainToNetworkData(chain: ChainWithRpcUrl) { explorerUrls[chain.chainId] = chain.explorerUrl } -function isL1Chain(chain: { chainId: number }): chain is L1Network { - return typeof l1Networks[chain.chainId] !== 'undefined' -} - function isArbitrumChain( - chain: L1Network | ArbitrumNetwork + chain: BlockNumberReferenceNetwork | ArbitrumNetwork ): chain is ArbitrumNetwork { return typeof (chain as ArbitrumNetwork).parentChainId !== 'undefined' } +function isBlockNumberReferenceNetwork(chain: { + chainId: number +}): chain is BlockNumberReferenceNetwork { + return ( + typeof l1Networks[chain.chainId] !== 'undefined' || + typeof baseNetworks[chain.chainId] !== 'undefined' + ) +} + export const TELEPORT_ALLOWLIST: { [id: number]: number[] } = { [ChainId.Ethereum]: [1380012617, 70700, 70701], // Rari, PopApex and PopBoss [ChainId.Sepolia]: [1918988905] // RARI Testnet } -export function getChildChainIds(chain: ArbitrumNetwork | L1Network) { +export function getChildChainIds( + chain: ArbitrumNetwork | BlockNumberReferenceNetwork +) { const childChainIds = [ ...getChildrenForNetwork(chain.chainId).map(chain => chain.chainId), ...(TELEPORT_ALLOWLIST[chain.chainId] ?? []) // for considering teleport (L1-L3 transfers) we will get the L3 children of the chain, if present diff --git a/packages/arb-token-bridge-ui/src/util/orbitChainsData.json b/packages/arb-token-bridge-ui/src/util/orbitChainsData.json index baad064429..406f89a78b 100644 --- a/packages/arb-token-bridge-ui/src/util/orbitChainsData.json +++ b/packages/arb-token-bridge-ui/src/util/orbitChainsData.json @@ -880,6 +880,56 @@ "logoUrl": "/images/xrTokenLogo.png" } } + }, + { + "chainId": 631571, + "confirmPeriodBlocks": 900, + "ethBridge": { + "bridge": "0x133634FA8F372e59422744759a67796F01428BDD", + "inbox": "0x81Bf51dEC736adA7E256ed6C092CAe24D62a1ca7", + "outbox": "0x020e1cb41cF68999D988c20Ef40E2C58e1947db2", + "rollup": "0xAFcb6cF53AB01ac39577A592E141F614171c9371", + "sequencerInbox": "0x2453eb0d6F58C01Adb87505A4348fB5EEceB007D" + }, + "nativeToken": "0xe97f36a00058AA7DfC4E85d23532C3f70453a7aE", + "explorerUrl": "https://polter-testnet.explorer.alchemy.com", + "rpcUrl": "https://geist-polter.g.alchemy.com/public", + "isCustom": true, + "isTestnet": true, + "name": "Polter Testnet", + "slug": "polter-testnet", + "parentChainId": 84532, + "retryableLifetimeSeconds": 604800, + "tokenBridge": { + "parentCustomGateway": "0x24e1e6375f0f0Eecd38266357cADE57cA3A348F3", + "parentErc20Gateway": "0x9014E7244116b965F0D1059f80e2bD8169957117", + "parentGatewayRouter": "0xE38553a7989feD9e240DE8B0f5ed166BF75a088A", + "parentMultiCall": "0xE0753Df74d86D6B25aCd2d049389c7E52e2dd728", + "parentProxyAdmin": "0x0000000000000000000000000000000000000000", + "parentWeth": "0x0000000000000000000000000000000000000000", + "parentWethGateway": "0x0000000000000000000000000000000000000000", + "childCustomGateway": "0x7965162499751ABf89b8e3C640f8229AD8EeB022", + "childErc20Gateway": "0xA8E8b5D957e4ad9335E504442e877E513BD50729", + "childGatewayRouter": "0xF654086B1118fE98ea045De8D6dC0F1C53Da5B76", + "childMultiCall": "0xb97c5bC7DB7532471726550480A855484a408d00", + "childProxyAdmin": "0xda11A4ecaAcA51b1f2e2109a2EEFaC438409b68D", + "childWeth": "0x0000000000000000000000000000000000000000", + "childWethGateway": "0x0000000000000000000000000000000000000000" + }, + "bridgeUiConfig": { + "color": "#03AB2A", + "network": { + "name": "Polter Testnet", + "logo": "/images/PolterTestnetLogo.png", + "description": "A gaming testnet for Aavegotchi's Geist Mainnet." + }, + "nativeTokenData": { + "name": "Aavegotchi GHST Token", + "symbol": "GHST", + "decimals": 18, + "logoUrl": "/images/ghst.png" + } + } } ] } diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts b/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts index bd6774c656..66de1b1b0f 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/getWagmiChain.ts @@ -9,7 +9,9 @@ import { arbitrumSepolia, localL1Network, localL2Network, - localL3Network + localL3Network, + baseSepolia, + base } from './wagmiAdditionalNetworks' import { ChainId } from '../networks' import { getCustomChainFromLocalStorageById } from '../networks' @@ -37,6 +39,9 @@ export function getWagmiChain(chainId: number): Chain { case ChainId.ArbitrumNova: return arbitrumNova + case ChainId.Base: + return base + // Testnets case ChainId.Sepolia: return sepolia @@ -47,6 +52,9 @@ export function getWagmiChain(chainId: number): Chain { case ChainId.ArbitrumSepolia: return arbitrumSepolia + case ChainId.BaseSepolia: + return baseSepolia + // Local networks case ChainId.Local: return localL1Network diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts b/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts index 0023574b53..fac6d8c971 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/setup.ts @@ -11,7 +11,9 @@ import { localL1Network as local, localL2Network as arbitrumLocal, localL3Network as l3Local, - holesky + holesky, + base, + baseSepolia } from './wagmiAdditionalNetworks' import { isTestingEnvironment } from '../CommonUtils' import { getCustomChainsFromLocalStorage, ChainId } from '../networks' @@ -26,16 +28,22 @@ const wagmiOrbitChains = getOrbitChains().map(chain => getWagmiChain(chain.chainId) ) +const defaultChains = [ + // mainnet, arb1, & arb nova are for network switch tests + mainnet, + arbitrum, + arbitrumNova, + base, + // sepolia & arb sepolia are for tx history panel tests + sepolia, + arbitrumSepolia, + baseSepolia, + holesky +] + const chainList = isTestingEnvironment ? [ - // mainnet, arb1, & arb nova are for network switch tests - mainnet, - arbitrum, - arbitrumNova, - // sepolia & arb sepolia are for tx history panel tests - sepolia, - arbitrumSepolia, - holesky, + ...defaultChains, // Orbit chains ...wagmiOrbitChains, // add local environments during testing @@ -45,16 +53,7 @@ const chainList = isTestingEnvironment // user-added custom chains ...customChains ] - : [ - mainnet, - arbitrum, - arbitrumNova, - sepolia, - arbitrumSepolia, - holesky, - ...wagmiOrbitChains, - ...customChains - ] + : [...defaultChains, ...wagmiOrbitChains, ...customChains] const projectId = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID! @@ -71,8 +70,10 @@ enum TargetChainKey { Ethereum = 'mainnet', ArbitrumOne = 'arbitrum-one', ArbitrumNova = 'arbitrum-nova', + Base = 'base', Sepolia = 'sepolia', - ArbitrumSepolia = 'arbitrum-sepolia' + ArbitrumSepolia = 'arbitrum-sepolia', + BaseSepolia = 'base-sepolia' } function sanitizeTargetChainKey(targetChainKey: string | null): TargetChainKey { @@ -100,11 +101,17 @@ function getChainId(targetChainKey: TargetChainKey): number { case TargetChainKey.ArbitrumNova: return ChainId.ArbitrumNova + case TargetChainKey.Base: + return ChainId.Base + case TargetChainKey.Sepolia: return ChainId.Sepolia case TargetChainKey.ArbitrumSepolia: return ChainId.ArbitrumSepolia + + case TargetChainKey.BaseSepolia: + return ChainId.BaseSepolia } } diff --git a/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts b/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts index 4ab0f3b6a9..857f4f8bee 100644 --- a/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts +++ b/packages/arb-token-bridge-ui/src/util/wagmi/wagmiAdditionalNetworks.ts @@ -85,6 +85,28 @@ export const arbitrumSepolia: Chain = { } } +export const baseSepolia: Chain = { + id: ChainId.BaseSepolia, + name: 'Base Sepolia', + network: 'base-sepolia', + nativeCurrency: ether, + rpcUrls: { + default: { + http: [rpcURLs[ChainId.BaseSepolia]!] + }, + public: { + http: [rpcURLs[ChainId.BaseSepolia]!] + } + }, + blockExplorers: { + etherscan: { + name: 'Basescan', + url: explorerUrls[ChainId.BaseSepolia]! + }, + default: { name: 'Basescan', url: explorerUrls[ChainId.BaseSepolia]! } + } +} + export const arbitrumNova: Chain = { id: ChainId.ArbitrumNova, name: 'Arbitrum Nova', @@ -104,6 +126,25 @@ export const arbitrumNova: Chain = { } } +export const base: Chain = { + id: ChainId.Base, + name: 'Base', + network: 'base', + nativeCurrency: ether, + rpcUrls: { + default: { + http: [rpcURLs[ChainId.Base]!] + }, + public: { + http: [rpcURLs[ChainId.Base]!] + } + }, + blockExplorers: { + etherscan: { name: 'Basescan', url: explorerUrls[ChainId.Base]! }, + default: { name: 'Basescan', url: explorerUrls[ChainId.Base]! } + } +} + /** * For e2e testing */