Skip to content

Commit

Permalink
feat: support bitlayer mainnet (#4)
Browse files Browse the repository at this point in the history
* feat: support bitlayer mainnet

* fix: graphql fetch

* refactor: filter supported chains
  • Loading branch information
JayJay1024 authored Jul 11, 2024
1 parent 1044c58 commit bb3773f
Show file tree
Hide file tree
Showing 26 changed files with 301 additions and 181 deletions.
1 change: 1 addition & 0 deletions apps/web/public/csp.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"https://rpc.ankr.com",
"https://rpc.blast.io/",
"https://testnet-rpc.bitlayer.org",
"https://rpc.bitlayer.org",
"https://rpc.degen.tips",
"https://rpc.goerli.mudit.blog/",
"https://rpc.mevblocker.io/",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Currency } from '@uniswap/sdk-core'
import { NATIVE_CURRENCY } from '@uniswap/smart-order-router'
import { SupportedInterfaceChainId, chainIdToBackendChain } from 'constants/chains'
import { COMMON_BASES } from 'constants/routing'
import { isBitlayer, NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { apolloClient } from 'graphql/data/apollo/client'
import { gqlTokenToCurrencyInfo } from 'graphql/data/types'
import {
Expand All @@ -27,7 +28,7 @@ export async function getCurrency(
currencyId === NATIVE_CHAIN_ID ||
currencyId?.toLowerCase() === "native" ||
currencyId?.toLowerCase() === "eth" ||
(isBitlayer(chainId) && currencyId?.toLowerCase() === "btc");
currencyId?.toLowerCase() === NATIVE_CURRENCY[chainId ?? -1]?.toLowerCase()
if (isNative) {
return nativeOnChain(chainId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
Token,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES as V3NFT_ADDRESSES,
} from '@uniswap/sdk-core'
import type { AddressMap } from '@uniswap/smart-order-router'
import { isChainSupportedByHelixSwap, type AddressMap } from '@uniswap/smart-order-router'
import NFTPositionManagerJSON from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import MulticallJSON from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
import { useWeb3React } from '@web3-react/core'
Expand Down Expand Up @@ -69,7 +69,7 @@ export function useInterfaceMulticallContracts(chainIds: ChainId[]): ContractMap
type PriceMap = { [key: CurrencyKey]: number | undefined }
export function usePoolPriceMap(positions: PositionInfo[] | undefined) {
const contracts = useMemo(() => {
if (!positions || !positions.length) {
if (!positions || !positions.length || positions.some((p) => isChainSupportedByHelixSwap(p.chainId))) {
return []
}
// Avoids fetching duplicate tokens by placing in map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { currencyKey } from 'utils/currencyKey'
import { PositionInfo, useCachedPositions, useGetCachedTokens, usePoolAddressCache } from './cache'
import { Call, DEFAULT_GAS_LIMIT } from './getTokensAsync'
import { useInterfaceMulticallContracts, usePoolPriceMap, useV3ManagerContracts } from './hooks'
import { isBitlayer } from 'constants/tokens'
import { isChainSupportedByHelixSwap } from '@uniswap/smart-order-router'

function createPositionInfo(
owner: string,
Expand Down Expand Up @@ -43,7 +43,7 @@ const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)

const DEFAULT_CHAINS = [...L1_CHAIN_IDS, ...L2_CHAIN_IDS].filter((chain: number) => {
// return !TESTNET_CHAIN_IDS.includes(chain)
return isBitlayer(chain)
return isChainSupportedByHelixSwap(chain)
})

type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean }
Expand Down
6 changes: 6 additions & 0 deletions apps/web/src/components/Logo/ChainLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ export function getChainUI(chainId: ChainId, darkMode: boolean): ChainUI | undef
bgColor: '#6B8AFF33',
textColor: '#6B8AFF',
}
case ChainId.BITLAYER:
return {
symbol: BITLAYER_LOGO,
bgColor: '#6B8AFF33',
textColor: '#6B8AFF',
}
default:
return undefined
}
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/NavBar/ChainSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { Connector } from 'wagmi'
import ChainSelectorRow from './ChainSelectorRow'
import { isChainSupportedByHelixSwap } from '@uniswap/smart-order-router'

const NETWORK_SELECTOR_CHAINS = [...L1_CHAIN_IDS, ...L2_CHAIN_IDS]
const NETWORK_SELECTOR_CHAINS = [...L1_CHAIN_IDS, ...L2_CHAIN_IDS].filter(isChainSupportedByHelixSwap)

const StyledDropdownButton = css`
display: flex;
Expand Down
68 changes: 42 additions & 26 deletions apps/web/src/components/Pools/PoolTable/PoolTable.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApolloError } from '@apollo/client'
import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { InterfaceElementName } from '@uniswap/analytics-events'
import { ChainId, CurrencyAmount, Percent, Token as CoreToken } from '@uniswap/sdk-core'
import { BRC_BITLAYER_TESTNET, USDC_BITLAYER_TESTNET, USDT_BITLAYER_TESTNET, WBTC_BITLAYER_TESTNET } from '@uniswap/smart-order-router'
import { ChainId, CurrencyAmount, Percent, Token as CoreToken, Currency } from '@uniswap/sdk-core'
import { BRC_BITLAYER_TESTNET, USDC_BITLAYER, USDC_BITLAYER_TESTNET, USDT_BITLAYER_TESTNET, WBTC_BITLAYER, WBTC_BITLAYER_TESTNET } from '@uniswap/smart-order-router'
import { FeeAmount, Pool, Position } from '@uniswap/v3-sdk'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { DoubleCurrencyAndChainLogo } from 'components/DoubleLogo'
Expand All @@ -27,7 +27,6 @@ import { useAtom } from 'jotai'
import { atomWithReset, useAtomValue, useResetAtom, useUpdateAtom } from 'jotai/utils'
import { ReactElement, ReactNode, useCallback, useEffect, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { useSwapAndLimitContext } from 'state/swap/hooks'
import styled from 'styled-components'
import { ThemedText } from 'theme/components'
import { PositionDetails } from 'types/position'
Expand Down Expand Up @@ -409,32 +408,49 @@ export function PoolsTable({
)
}

function getPoolKeys(chainId: ChainId | undefined): [Currency, Currency, FeeAmount][] {
switch (chainId) {
case ChainId.BITLAYER_TESTNET:
return [
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(USDT_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(WBTC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(BRC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDT_BITLAYER_TESTNET),
unwrappedToken(WBTC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
]
case ChainId.BITLAYER:
return [
[
unwrappedToken(USDC_BITLAYER),
unwrappedToken(WBTC_BITLAYER),
FeeAmount.LOW,
],
]
default:
return []
}
}

export function ChainAllPoolsTable() {
const account = useAccount();
const { chainId } = useSwapAndLimitContext();
const chain = useChainFromUrlParam()

const pools = usePools([
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(USDT_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(WBTC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDC_BITLAYER_TESTNET),
unwrappedToken(BRC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
[
unwrappedToken(USDT_BITLAYER_TESTNET),
unwrappedToken(WBTC_BITLAYER_TESTNET),
FeeAmount.LOW,
],
]);
const pools = usePools(getPoolKeys(chain?.id ?? account.chainId))
const { positions: userPositions, loading: userPositionsLoading } =
useV3Positions(account?.address);
const filteredUserPositions = useFilterPossiblyMaliciousPositions(
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/constants/chains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const chainPriorityTestCases: [ChainId, number][] = [
[ChainId.BLAST, 8],
[ChainId.ZORA, 9],
[ChainId.BITLAYER_TESTNET, 9],
[ChainId.BITLAYER, 9],
]

test.each(chainPriorityTestCases)(
Expand Down Expand Up @@ -70,6 +71,7 @@ const chainIdNames: { [chainId in SupportedInterfaceChainId]: string } = {
[ChainId.BLAST]: 'blast',
[ChainId.ZORA]: 'zora',
[ChainId.BITLAYER_TESTNET]: 'bitlayer_testnet',
[ChainId.BITLAYER]: 'bitlayer',
} as const

test.each(Object.keys(chainIdNames).map((key) => parseInt(key) as SupportedInterfaceChainId))(
Expand Down
14 changes: 10 additions & 4 deletions apps/web/src/constants/chains.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable rulesdir/no-undefined-or */
import { ChainId, Currency, V2_ROUTER_ADDRESSES } from '@uniswap/sdk-core'
import { isChainSupportedByHelixSwap } from '@uniswap/smart-order-router'
import ms from 'ms'
import { useCallback, useMemo } from 'react'
import { useParams } from 'react-router-dom'
import { useParams, useSearchParams } from 'react-router-dom'
import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains'
import { Chain as BackendChainId } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
Expand All @@ -29,10 +30,11 @@ export const SUPPORTED_INTERFACE_CHAIN_IDS = [
ChainId.BLAST,
ChainId.ZORA,
ChainId.BITLAYER_TESTNET,
ChainId.BITLAYER,
] as const

export function isSupportedChainId(chainId?: number | ChainId | null): chainId is SupportedInterfaceChainId {
return !!chainId && SUPPORTED_INTERFACE_CHAIN_IDS.includes(chainId as SupportedInterfaceChainId)
return !!chainId && SUPPORTED_INTERFACE_CHAIN_IDS.filter(isChainSupportedByHelixSwap).includes(chainId as SupportedInterfaceChainId)
}

// Used to feature flag chains. If a chain is not included in the object, it is considered enabled by default.
Expand Down Expand Up @@ -71,7 +73,7 @@ export function useIsSupportedChainIdCallback() {

export function useSupportedChainId(chainId?: number): SupportedInterfaceChainId | undefined {
const featureFlaggedChains = useFeatureFlaggedChainIds()
if (!chainId || SUPPORTED_INTERFACE_CHAIN_IDS.indexOf(chainId) === -1) {
if (!chainId || SUPPORTED_INTERFACE_CHAIN_IDS.filter(isChainSupportedByHelixSwap).indexOf(chainId) === -1) {
return
}

Expand All @@ -98,6 +100,7 @@ const POLYGON_MUMBAI = UNIVERSE_CHAIN_INFO[UniverseChainId.PolygonMumbai]
const SEPOLIA = UNIVERSE_CHAIN_INFO[UniverseChainId.SEPOLIA]
const ZORA = UNIVERSE_CHAIN_INFO[UniverseChainId.ZORA]
const BITLAYER_TESTNET = UNIVERSE_CHAIN_INFO[UniverseChainId.BITLAYER_TESTNET]
const BITLAYER = UNIVERSE_CHAIN_INFO[UniverseChainId.BITLAYER]

const INTERFACE_SUPPORTED_CHAINS = [
MAINNET,
Expand All @@ -117,6 +120,7 @@ const INTERFACE_SUPPORTED_CHAINS = [
BLAST,
ZORA,
BITLAYER_TESTNET,
BITLAYER,
] as const

type ExtractObject<TObject extends Record<string, unknown>, TNarrowedObject extends Partial<TObject>> = Extract<
Expand Down Expand Up @@ -149,6 +153,7 @@ export const CHAIN_INFO: ChainInfoMap = {
[ChainId.BLAST]: BLAST,
[ChainId.ZORA]: ZORA,
[ChainId.BITLAYER_TESTNET]: BITLAYER_TESTNET,
[ChainId.BITLAYER]: BITLAYER,
} as const

export type ChainSlug = SupportedInterfaceChain['urlParam']
Expand Down Expand Up @@ -307,5 +312,6 @@ export function useChainFromUrlParam(): SupportedInterfaceChain | undefined {
const chainName = useParams<{ chainName?: string }>().chainName
// In the case where /explore/:chainName is used, the chainName is passed as a tab param
const tab = useParams<{ tab?: string }>().tab
return getChainFromChainUrlParam(getChainUrlParam(chainName ?? tab))
const chain = useSearchParams()[0].get('chain')
return getChainFromChainUrlParam(getChainUrlParam(chainName ?? tab ?? chain ?? ''))
}
8 changes: 7 additions & 1 deletion apps/web/src/constants/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import {
WRAPPED_NATIVE_CURRENCY,
nativeOnChain,
} from './tokens'
import { BRC_BITLAYER_TESTNET } from '@uniswap/smart-order-router'
import { BRC_BITLAYER_TESTNET, USDC_BITLAYER, USDT_BITLAYER } from '@uniswap/smart-order-router'

type ChainTokenList = {
readonly [chainId: number]: Token[]
Expand Down Expand Up @@ -179,6 +179,12 @@ export const COMMON_BASES: ChainCurrencyList = {
WRAPPED_NATIVE_CURRENCY[ChainId.BITLAYER_TESTNET] as Token,
BRC_BITLAYER_TESTNET,
].map(buildCurrencyInfo),
[ChainId.BITLAYER]: [
nativeOnChain(ChainId.BITLAYER),
USDT_BITLAYER,
USDC_BITLAYER,
WRAPPED_NATIVE_CURRENCY[ChainId.BITLAYER] as Token,
].map(buildCurrencyInfo),
}

// used to construct the list of all pairs we consider by default in the frontend
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/constants/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,8 @@ export function isCelo(chainId: number): chainId is ChainId.CELO | ChainId.CELO_
return chainId === ChainId.CELO_ALFAJORES || chainId === ChainId.CELO
}

export function isBitlayer(chainId: number): chainId is ChainId.BITLAYER_TESTNET {
return chainId === ChainId.BITLAYER_TESTNET
export function isBitlayer(chainId: number): chainId is ChainId.BITLAYER_TESTNET | ChainId.BITLAYER {
return chainId === ChainId.BITLAYER_TESTNET || chainId === ChainId.BITLAYER
}

function getCeloNativeCurrency(chainId: number) {
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/hooks/Tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { currencyId } from 'utils/currencyId'
import { getNativeTokenDBAddress } from 'utils/nativeTokens'
import { useCombinedInactiveLists } from '../state/lists/hooks'
import { useUserAddedTokens } from '../state/user/userAddedTokens'
import { NATIVE_CURRENCY } from '@uniswap/smart-order-router'

type Maybe<T> = T | undefined

Expand Down Expand Up @@ -116,7 +117,8 @@ export function useCurrencyInfo(
address === NATIVE_CHAIN_ID ||
address?.toLowerCase() === "native" ||
address?.toLowerCase() === "eth" ||
address?.toLowerCase() === "btc";
address?.toLowerCase() === "btc" ||
address?.toLowerCase() === NATIVE_CURRENCY[chainIdWithFallback ?? -1]?.toLowerCase();

const commonBase = chainIdWithFallback
? COMMON_BASES[chainIdWithFallback]?.find(
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/hooks/usePoolTickData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useAllV3TicksQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__
import { logger } from 'utilities/src/logger/logger'
import computeSurroundingTicks from 'utils/computeSurroundingTicks'
import { PoolState, usePoolMultichain } from './usePools'
import { isChainSupportedByHelixSwap } from '@uniswap/smart-order-router'

const PRICE_FIXED_DIGITS = 8

Expand Down Expand Up @@ -52,7 +53,7 @@ function useTicksFromSubgraph(
skip,
first: MAX_TICK_FETCH_VALUE,
},
skip: !poolAddress || chainId === ChainId.BITLAYER_TESTNET,
skip: !poolAddress || isChainSupportedByHelixSwap(chainId),
pollInterval: ms(`30s`),
})
}
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/hooks/useUSDPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function getEthAmountOutRaw(chainId: SupportedInterfaceChainId) {
case ChainId.MAINNET:
return 50e18
case ChainId.BITLAYER_TESTNET:
case ChainId.BITLAYER:
return 10_000
default:
return 10e18
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/lib/hooks/useCurrencyBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useAccount } from 'hooks/useAccount'
import { isAddress } from 'utilities/src/addresses'
import { nativeOnChain } from '../../constants/tokens'
import { useInterfaceMulticall } from '../../hooks/useContract'
import { isChainSupportedByHelixSwap } from '@uniswap/smart-order-router'

/**
* Returns a map of the given addresses to their eventually consistent ETH balances.
Expand Down Expand Up @@ -183,7 +184,7 @@ export function useCurrencyBalances(
chainId?: number
): (CurrencyAmount<Currency> | undefined)[] {
const { chainId: providerChainId } = useAccount()
const isSynced = chainId === providerChainId || providerChainId === ChainId.BITLAYER_TESTNET
const isSynced = chainId === providerChainId || isChainSupportedByHelixSwap(providerChainId ?? -1)

const gqlCurrencyBalances = useGqlCurrencyBalances(account, currencies)
const rpcCurrencyBalances = useRpcCurrencyBalances(account, currencies)
Expand Down
9 changes: 5 additions & 4 deletions apps/web/src/lib/hooks/useCurrencyLogoURIs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import BRCLogo from '../../assets/svg/brc.svg'
import BTCLogo from '../../assets/svg/btc.svg'
import WBTCLogo from '../../assets/images/wbtc.png'
import { PORTAL_ETH_CELO, isBitlayer, isCelo, nativeOnChain } from '../../constants/tokens'
import { BRC_BITLAYER_TESTNET, USDC_BITLAYER_TESTNET, USDT_BITLAYER_TESTNET, WBTC_BITLAYER_TESTNET } from '@uniswap/smart-order-router'
import { BRC_BITLAYER_TESTNET, USDC_BITLAYER, USDC_BITLAYER_TESTNET, USDT_BITLAYER, USDT_BITLAYER_TESTNET, WBTC_BITLAYER, WBTC_BITLAYER_TESTNET } from '@uniswap/smart-order-router'

export function getNativeLogoURI(chainId: ChainId = ChainId.MAINNET): string {
switch (chainId) {
Expand All @@ -26,6 +26,7 @@ export function getNativeLogoURI(chainId: ChainId = ChainId.MAINNET): string {
case ChainId.AVALANCHE:
return AvaxLogo
case ChainId.BITLAYER_TESTNET:
case ChainId.BITLAYER:
return BTCLogo
default:
return EthereumLogo
Expand All @@ -42,13 +43,13 @@ export function getTokenLogoURI(address: string, chainId: ChainId = ChainId.MAIN
return EthereumLogo
}

if (isBitlayer(chainId) && isSameAddress(address, USDC_BITLAYER_TESTNET.address)) {
if (isBitlayer(chainId) && (isSameAddress(address, USDC_BITLAYER_TESTNET.address) || isSameAddress(address, USDC_BITLAYER.address))) {
return 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png'
}
if (isBitlayer(chainId) && isSameAddress(address, USDT_BITLAYER_TESTNET.address)) {
if (isBitlayer(chainId) && (isSameAddress(address, USDT_BITLAYER_TESTNET.address) || isSameAddress(address, USDT_BITLAYER.address))) {
return 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png'
}
if (isBitlayer(chainId) && isSameAddress(address, WBTC_BITLAYER_TESTNET.address)) {
if (isBitlayer(chainId) && (isSameAddress(address, WBTC_BITLAYER_TESTNET.address) || isSameAddress(address, WBTC_BITLAYER.address))) {
return WBTCLogo
}
if (isBitlayer(chainId) && isSameAddress(address, BRC_BITLAYER_TESTNET.address)) {
Expand Down
Loading

0 comments on commit bb3773f

Please sign in to comment.