Skip to content

Commit

Permalink
Merge pull request #47 from Uniswap/feat/base-support
Browse files Browse the repository at this point in the history
feat: add base mainnet support
  • Loading branch information
just-toby authored Aug 3, 2023
2 parents 1b89500 + 51bc5eb commit 8cdd7d5
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/constants/chainId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export enum ChainId {

AVALANCHE = 43114,

BASE = 8453,
BASE_GOERLI = 84531,
}
16 changes: 16 additions & 0 deletions src/constants/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export const COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI = new Token(
'Coinbase Wrapped Staked ETH'
)

export const COINBASE_WRAPPED_STAKED_ETH_BASE = new Token(
ChainId.BASE,
'0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22',
18,
'cbETH',
'Coinbase Wrapped Staked ETH'
)

export const COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE = new Token(
ChainId.ARBITRUM_ONE,
'0x1DEBd73E752bEaF79865Fd6446b0c970EaE7732f',
Expand Down Expand Up @@ -78,6 +86,14 @@ export const DAI_AVALANCHE = new Token(
'DAI.e'
)

export const DAI_BASE = new Token(
ChainId.BASE,
'0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
18,
'DAI',
'Dai Stablecoin'
)

export const DAI_BASE_GOERLI = new Token(
ChainId.BASE_GOERLI,
'0x174956bDfbCEb6e53089297cce4fE2825E58d92C',
Expand Down
36 changes: 36 additions & 0 deletions src/fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import {
COINBASE_WRAPPED_STAKED_ETH,
COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI,
COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE,
COINBASE_WRAPPED_STAKED_ETH_BASE,
DAI_BASE_GOERLI,
COINBASE_WRAPPED_STAKED_ETH_OPTIMISM,
DAI_BASE,
} from '../constants/tokens'
import { compareTokenInfos } from '../utils'

Expand Down Expand Up @@ -46,6 +48,12 @@ export const Tokens: Partial<Record<ChainId, Record<string, TokenInfo>>> = {
[ChainId.AVALANCHE]: {
DAI: tokenToTokenInfo(DAI_AVALANCHE),
},
[ChainId.BASE]: {
DAI: tokenToTokenInfo(DAI_BASE),
COINBASE_WRAPPED_STAKED_ETH: tokenToTokenInfo(
COINBASE_WRAPPED_STAKED_ETH_BASE
),
},
[ChainId.BASE_GOERLI]: {
DAI: tokenToTokenInfo(DAI_BASE_GOERLI),
COINBASE_WRAPPED_STAKED_ETH: tokenToTokenInfo(
Expand Down Expand Up @@ -256,6 +264,34 @@ export const baseGoerliSampleTokenList_3 = {
].sort(compareTokenInfos),
}

export const baseSampleTokenList_3 = {
...sampleL1TokenList_3,
name: 'Base Sample_3',
tokens: [
{
...Tokens[ChainId.BASE]!.COINBASE_WRAPPED_STAKED_ETH,
extensions: {
bridgeInfo: {
[ChainId.MAINNET]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH.address,
},
},
},
} as unknown as TokenInfo,
{
...(Tokens[ChainId.MAINNET]!
.COINBASE_WRAPPED_STAKED_ETH as unknown as TokenInfo),
extensions: {
bridgeInfo: {
[ChainId.BASE]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_BASE.address,
},
},
},
} as unknown as TokenInfo,
].sort(compareTokenInfos),
}

export const optimizedSampleTokenList = {
...sampleL1TokenList,
name: 'Optimized Sample',
Expand Down
46 changes: 46 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { ChainId } from './constants/chainId'
import {
COINBASE_WRAPPED_STAKED_ETH,
COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE,
COINBASE_WRAPPED_STAKED_ETH_BASE,
COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI,
COINBASE_WRAPPED_STAKED_ETH_OPTIMISM,
DAI,
DAI_ARBITRUM_ONE,
DAI_AVALANCHE,
DAI_BASE,
DAI_BASE_GOERLI,
DAI_BNB,
DAI_OPTIMISM,
Expand All @@ -26,6 +28,7 @@ import {
avalanchedSampleTokenList,
sampleL1TokenList_3,
baseGoerliSampleTokenList_3,
baseSampleTokenList_3,
} from './fixtures'

jest.setTimeout(15000)
Expand Down Expand Up @@ -162,6 +165,22 @@ it('outputs base goerli list correctly', async () => {
)
})

it('outputs base list correctly', async () => {
const tokenList = await chainifyTokenList([ChainId.BASE], sampleL1TokenList_3)
expect(tokenList).toBeDefined()
expect(tokenList?.version).toEqual(baseSampleTokenList_3.version)
expect(
tokenList?.tokens.map((t) => [t.address, t.chainId, t.extensions])
).toEqual(
// ignores other metadata
baseSampleTokenList_3.tokens.map((t) => [
t.address,
t.chainId,
t.extensions,
])
)
})

describe(mergeTokenLists, () => {
it('correctly deduplicates', () => {
const merged = mergeTokenLists(sampleL1TokenList, sampleL1TokenList)
Expand Down Expand Up @@ -282,6 +301,9 @@ describe(chainify, () => {
// destBridgeAddress: arbBridgeL2Address,
// originBridgeAddress: arbBridgeL1Address,
},
[ChainId.BASE]: {
tokenAddress: DAI_BASE.address,
},
[ChainId.BASE_GOERLI]: {
tokenAddress: DAI_BASE_GOERLI.address,
},
Expand Down Expand Up @@ -319,6 +341,16 @@ describe(chainify, () => {
},
},
},
{
...Tokens[ChainId.BASE]!.DAI,
extensions: {
bridgeInfo: {
[ChainId.MAINNET]: {
tokenAddress: DAI.address,
},
},
},
},
{
...Tokens[ChainId.ARBITRUM_ONE]!.DAI,
extensions: {
Expand Down Expand Up @@ -368,6 +400,9 @@ describe(chainify, () => {
[ChainId.OPTIMISM]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_OPTIMISM.address,
},
[ChainId.BASE]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_BASE.address,
},
[ChainId.BASE_GOERLI]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI.address,
},
Expand All @@ -388,6 +423,17 @@ describe(chainify, () => {
},
},
},
{
...Tokens[ChainId.BASE]!.COINBASE_WRAPPED_STAKED_ETH,
name: 'Coinbase Wrapped Staked ETH',
extensions: {
bridgeInfo: {
[ChainId.MAINNET]: {
tokenAddress: COINBASE_WRAPPED_STAKED_ETH.address,
},
},
},
},
{
...Tokens[ChainId.ARBITRUM_ONE]!.COINBASE_WRAPPED_STAKED_ETH,
name: 'Coinbase Wrapped Staked ETH',
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export async function chainify(
ChainId.CELO,
ChainId.BNB,
ChainId.AVALANCHE,
ChainId.BASE,
ChainId.BASE_GOERLI,
]

Expand Down
42 changes: 42 additions & 0 deletions src/providers/BaseMappingProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MappingProvider } from './MappingProvider'
import { ChainId } from '../constants/chainId'
import { getTokenList } from '../utils'
import { GenericMappedTokenData } from '../constants/types'

const baseGoerliTokenListURL =
'https://raw.githubusercontent.com/' +
'ethereum-optimism/ethereum-optimism.github.io/master/optimism.tokenlist.json'

/**
* The Base mapping (linked above) is manually maintained by the Coinbase team
* in this repository: https://github.com/ethereum-optimism/ethereum-optimism.github.io.
*/
export class BaseMappingProvider implements MappingProvider {
async provide(): Promise<GenericMappedTokenData> {
const tokens: { [key: string]: string | undefined } = {}

let allTokens = await getTokenList(baseGoerliTokenListURL)

let opTokenId_baseAddressMap: Record<string, string> = {}
allTokens.tokens.forEach((token) => {
if (token.chainId === ChainId.BASE) {
if (typeof token.extensions?.opTokenId === 'string') {
opTokenId_baseAddressMap[token.extensions.opTokenId] = token.address
}
}
})

allTokens.tokens.forEach((token) => {
if (
token.chainId === ChainId.MAINNET &&
typeof token.extensions?.opTokenId === 'string' &&
token.extensions!.opTokenId in opTokenId_baseAddressMap
) {
tokens[token.address.toLowerCase()] =
opTokenId_baseAddressMap[token.extensions!.opTokenId]
}
})

return tokens
}
}
7 changes: 5 additions & 2 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from '../constants/types'
import { AvalancheMappingProvider } from './AvalancheMappingProvider'
import { BaseGoerliMappingProvider } from './BaseGoerliMappingProvider'
import { BaseMappingProvider } from './BaseMappingProvider'

const web3 = new Web3()

Expand All @@ -32,6 +33,7 @@ const CHAINS_WITH_MAPPING_PROVIDERS = [
ChainId.OPTIMISM,
ChainId.BNB,
ChainId.AVALANCHE,
ChainId.BASE,
ChainId.BASE_GOERLI,
]

Expand Down Expand Up @@ -133,7 +135,7 @@ async function hasExistingTokenContract(address: string, chainId: ChainId) {
try {
const contract: Contract = new web3.eth.Contract(ERC20Abi, address)
await getTokenSymbolFromContract(contract)
} catch {
} catch (e) {
return false
}

Expand All @@ -152,6 +154,8 @@ function getMappingProvider(chainId: ChainId, l1TokenList: TokenList) {
return new BnbMappingProvider()
case ChainId.AVALANCHE:
return new AvalancheMappingProvider()
case ChainId.BASE:
return new BaseMappingProvider()
case ChainId.BASE_GOERLI:
return new BaseGoerliMappingProvider()
default:
Expand Down Expand Up @@ -227,7 +231,6 @@ async function getChildTokenDetails(
childToken && (chainId === ChainId.BNB || chainId === ChainId.AVALANCHE)
? (childToken as MappedToken).decimals
: undefined

return {
childTokenValid: childTokenValid,
childTokenAddress: childTokenAddress,
Expand Down
2 changes: 2 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export function getRpcUrl(chainId: ChainId): string {
return 'https://bsc-dataseed1.binance.org'
case ChainId.AVALANCHE:
return 'https://api.avax.network/ext/bc/C/rpc'
case ChainId.BASE:
return 'https://mainnet.base.org'
case ChainId.BASE_GOERLI:
return 'https://goerli.base.org'
default:
Expand Down

0 comments on commit 8cdd7d5

Please sign in to comment.