@uniswap/token-list-bridge-utils 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/chainId.d.ts +2 -1
- package/dist/constants/tokens.d.ts +3 -0
- package/dist/fixtures/index.d.ts +31 -0
- package/dist/providers/BaseGoerliMappingProvider.d.ts +9 -0
- package/dist/token-list-bridge-utils.cjs.development.js +85 -6
- package/dist/token-list-bridge-utils.cjs.development.js.map +1 -1
- package/dist/token-list-bridge-utils.cjs.production.min.js +1 -1
- package/dist/token-list-bridge-utils.cjs.production.min.js.map +1 -1
- package/dist/token-list-bridge-utils.esm.js +85 -6
- package/dist/token-list-bridge-utils.esm.js.map +1 -1
- package/package.json +3 -3
- package/src/constants/chainId.ts +2 -0
- package/src/constants/tokens.ts +24 -0
- package/src/fixtures/index.ts +51 -0
- package/src/index.test.ts +153 -0
- package/src/index.ts +10 -1
- package/src/providers/BaseGoerliMappingProvider.ts +43 -0
- package/src/providers/index.ts +4 -0
- package/src/utils.ts +3 -1
package/src/fixtures/index.ts
CHANGED
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
DAI_POLYGON,
|
|
11
11
|
USDT,
|
|
12
12
|
USDT_BNB,
|
|
13
|
+
COINBASE_WRAPPED_STAKED_ETH,
|
|
14
|
+
COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI,
|
|
15
|
+
COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE,
|
|
13
16
|
} from '../constants/tokens'
|
|
14
17
|
import { compareTokenInfos } from '../utils'
|
|
15
18
|
|
|
@@ -17,9 +20,13 @@ export const Tokens: Partial<Record<ChainId, Record<string, TokenInfo>>> = {
|
|
|
17
20
|
[ChainId.MAINNET]: {
|
|
18
21
|
DAI: tokenToTokenInfo(DAI),
|
|
19
22
|
USDT: tokenToTokenInfo(USDT),
|
|
23
|
+
COINBASE_WRAPPED_STAKED_ETH: tokenToTokenInfo(COINBASE_WRAPPED_STAKED_ETH),
|
|
20
24
|
},
|
|
21
25
|
[ChainId.ARBITRUM_ONE]: {
|
|
22
26
|
DAI: tokenToTokenInfo(DAI_ARBITRUM_ONE),
|
|
27
|
+
COINBASE_WRAPPED_STAKED_ETH: tokenToTokenInfo(
|
|
28
|
+
COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE
|
|
29
|
+
),
|
|
23
30
|
},
|
|
24
31
|
[ChainId.POLYGON]: {
|
|
25
32
|
DAI: tokenToTokenInfo(DAI_POLYGON),
|
|
@@ -34,6 +41,11 @@ export const Tokens: Partial<Record<ChainId, Record<string, TokenInfo>>> = {
|
|
|
34
41
|
[ChainId.AVALANCHE]: {
|
|
35
42
|
DAI: tokenToTokenInfo(DAI_AVALANCHE),
|
|
36
43
|
},
|
|
44
|
+
[ChainId.BASE_GOERLI]: {
|
|
45
|
+
COINBASE_WRAPPED_STAKED_ETH: tokenToTokenInfo(
|
|
46
|
+
COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI
|
|
47
|
+
),
|
|
48
|
+
},
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
export const sampleL1TokenList: TokenList = {
|
|
@@ -58,6 +70,17 @@ export const sampleL1TokenList_2: TokenList = {
|
|
|
58
70
|
tokens: [Tokens[ChainId.MAINNET]!.USDT],
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
export const sampleL1TokenList_3: TokenList = {
|
|
74
|
+
name: 'Sample_3',
|
|
75
|
+
timestamp: new Date(1646146610).toISOString(),
|
|
76
|
+
version: {
|
|
77
|
+
major: 1,
|
|
78
|
+
minor: 0,
|
|
79
|
+
patch: 0,
|
|
80
|
+
},
|
|
81
|
+
tokens: [Tokens[ChainId.MAINNET]!.COINBASE_WRAPPED_STAKED_ETH],
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
export const arbBridgeL2Address = '0x467194771dae2967aef3ecbedd3bf9a310c76c65'
|
|
62
85
|
export const arbBridgeL1Address = '0xd3b5b60020504bc3489d6949d545893982ba3011'
|
|
63
86
|
|
|
@@ -199,6 +222,34 @@ export const bnbedSampleTokenList_2 = {
|
|
|
199
222
|
].sort(compareTokenInfos),
|
|
200
223
|
}
|
|
201
224
|
|
|
225
|
+
export const baseGoerliSampleTokenList_3 = {
|
|
226
|
+
...sampleL1TokenList_3,
|
|
227
|
+
name: 'Base Goerli Sample_3',
|
|
228
|
+
tokens: [
|
|
229
|
+
{
|
|
230
|
+
...Tokens[ChainId.BASE_GOERLI]!.COINBASE_WRAPPED_STAKED_ETH,
|
|
231
|
+
extensions: {
|
|
232
|
+
bridgeInfo: {
|
|
233
|
+
[ChainId.MAINNET]: {
|
|
234
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH.address,
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
} as unknown as TokenInfo,
|
|
239
|
+
{
|
|
240
|
+
...(Tokens[ChainId.MAINNET]!
|
|
241
|
+
.COINBASE_WRAPPED_STAKED_ETH as unknown as TokenInfo),
|
|
242
|
+
extensions: {
|
|
243
|
+
bridgeInfo: {
|
|
244
|
+
[ChainId.BASE_GOERLI]: {
|
|
245
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI.address,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
} as unknown as TokenInfo,
|
|
250
|
+
].sort(compareTokenInfos),
|
|
251
|
+
}
|
|
252
|
+
|
|
202
253
|
export const optimizedSampleTokenList = {
|
|
203
254
|
...sampleL1TokenList,
|
|
204
255
|
name: 'Optimized Sample',
|
package/src/index.test.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { TokenInfo } from '@uniswap/token-lists'
|
|
|
2
2
|
import { chainify, chainifyTokenList, mergeTokenLists } from '.'
|
|
3
3
|
import { ChainId } from './constants/chainId'
|
|
4
4
|
import {
|
|
5
|
+
COINBASE_WRAPPED_STAKED_ETH,
|
|
6
|
+
COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE,
|
|
7
|
+
COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI,
|
|
5
8
|
DAI,
|
|
6
9
|
DAI_ARBITRUM_ONE,
|
|
7
10
|
DAI_AVALANCHE,
|
|
@@ -19,6 +22,8 @@ import {
|
|
|
19
22
|
sampleL1TokenList_2,
|
|
20
23
|
bnbedSampleTokenList_2,
|
|
21
24
|
avalanchedSampleTokenList,
|
|
25
|
+
sampleL1TokenList_3,
|
|
26
|
+
baseGoerliSampleTokenList_3,
|
|
22
27
|
} from './fixtures'
|
|
23
28
|
|
|
24
29
|
jest.setTimeout(15000)
|
|
@@ -136,6 +141,25 @@ it('outputs bnb list correctly with different decimals', async () => {
|
|
|
136
141
|
)
|
|
137
142
|
})
|
|
138
143
|
|
|
144
|
+
it('outputs base goerli list correctly', async () => {
|
|
145
|
+
const tokenList = await chainifyTokenList(
|
|
146
|
+
[ChainId.BASE_GOERLI],
|
|
147
|
+
sampleL1TokenList_3
|
|
148
|
+
)
|
|
149
|
+
expect(tokenList).toBeDefined()
|
|
150
|
+
expect(tokenList?.version).toEqual(baseGoerliSampleTokenList_3.version)
|
|
151
|
+
expect(
|
|
152
|
+
tokenList?.tokens.map((t) => [t.address, t.chainId, t.extensions])
|
|
153
|
+
).toEqual(
|
|
154
|
+
// ignores other metadata
|
|
155
|
+
baseGoerliSampleTokenList_3.tokens.map((t) => [
|
|
156
|
+
t.address,
|
|
157
|
+
t.chainId,
|
|
158
|
+
t.extensions,
|
|
159
|
+
])
|
|
160
|
+
)
|
|
161
|
+
})
|
|
162
|
+
|
|
139
163
|
describe(mergeTokenLists, () => {
|
|
140
164
|
it('correctly deduplicates', () => {
|
|
141
165
|
const merged = mergeTokenLists(sampleL1TokenList, sampleL1TokenList)
|
|
@@ -231,6 +255,93 @@ describe(mergeTokenLists, () => {
|
|
|
231
255
|
})
|
|
232
256
|
})
|
|
233
257
|
describe(chainify, () => {
|
|
258
|
+
it('creates tokenMap correctly in chainify', async () => {
|
|
259
|
+
const chainified = await chainify(sampleL1TokenList)
|
|
260
|
+
|
|
261
|
+
expect(chainified.tokenMap).toEqual({
|
|
262
|
+
'10_0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': {
|
|
263
|
+
...Tokens[ChainId.OPTIMISM]!.DAI,
|
|
264
|
+
name: 'Dai Stablecoin',
|
|
265
|
+
extensions: {
|
|
266
|
+
bridgeInfo: {
|
|
267
|
+
[ChainId.MAINNET]: {
|
|
268
|
+
tokenAddress: DAI.address,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
'137_0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063': {
|
|
274
|
+
...Tokens[ChainId.POLYGON]!.DAI,
|
|
275
|
+
extensions: {
|
|
276
|
+
bridgeInfo: {
|
|
277
|
+
[ChainId.MAINNET]: {
|
|
278
|
+
tokenAddress: DAI.address,
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
'1_0x6B175474E89094C44Da98b954EedeAC495271d0F': {
|
|
284
|
+
...Tokens[ChainId.MAINNET]!.DAI,
|
|
285
|
+
extensions: {
|
|
286
|
+
bridgeInfo: {
|
|
287
|
+
[ChainId.BNB]: {
|
|
288
|
+
tokenAddress: DAI_BNB.address,
|
|
289
|
+
},
|
|
290
|
+
[ChainId.AVALANCHE]: {
|
|
291
|
+
tokenAddress: DAI_AVALANCHE.address,
|
|
292
|
+
},
|
|
293
|
+
[ChainId.OPTIMISM]: {
|
|
294
|
+
tokenAddress: DAI_OPTIMISM.address,
|
|
295
|
+
},
|
|
296
|
+
[ChainId.POLYGON]: {
|
|
297
|
+
tokenAddress: DAI_POLYGON.address,
|
|
298
|
+
},
|
|
299
|
+
[ChainId.ARBITRUM_ONE]: {
|
|
300
|
+
tokenAddress: DAI_ARBITRUM_ONE.address,
|
|
301
|
+
// destBridgeAddress: arbBridgeL2Address,
|
|
302
|
+
// originBridgeAddress: arbBridgeL1Address,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
'42161_0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': {
|
|
308
|
+
...Tokens[ChainId.ARBITRUM_ONE]!.DAI,
|
|
309
|
+
extensions: {
|
|
310
|
+
bridgeInfo: {
|
|
311
|
+
[ChainId.MAINNET]: {
|
|
312
|
+
tokenAddress: DAI.address,
|
|
313
|
+
// originBridgeAddress: arbBridgeL2Address,
|
|
314
|
+
// destBridgeAddress: arbBridgeL1Address,
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
name: 'Dai Stablecoin',
|
|
319
|
+
},
|
|
320
|
+
'43114_0xd586E7F844cEa2F87f50152665BCbc2C279D8d70': {
|
|
321
|
+
...Tokens[ChainId.AVALANCHE]!.DAI,
|
|
322
|
+
name: 'Dai Stablecoin',
|
|
323
|
+
symbol: 'DAI',
|
|
324
|
+
extensions: {
|
|
325
|
+
bridgeInfo: {
|
|
326
|
+
[ChainId.MAINNET]: {
|
|
327
|
+
tokenAddress: DAI.address,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
'56_0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3': {
|
|
333
|
+
...Tokens[ChainId.BNB]!.DAI,
|
|
334
|
+
extensions: {
|
|
335
|
+
bridgeInfo: {
|
|
336
|
+
[ChainId.MAINNET]: {
|
|
337
|
+
tokenAddress: DAI.address,
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
234
345
|
it('provides bridge extensions', async () => {
|
|
235
346
|
const chainified = await chainify(sampleL1TokenList)
|
|
236
347
|
|
|
@@ -317,4 +428,46 @@ describe(chainify, () => {
|
|
|
317
428
|
},
|
|
318
429
|
])
|
|
319
430
|
})
|
|
431
|
+
|
|
432
|
+
it('provides bridge extensions for base goerli list', async () => {
|
|
433
|
+
const chainified = await chainify(sampleL1TokenList_3)
|
|
434
|
+
|
|
435
|
+
expect(chainified.tokens).toEqual([
|
|
436
|
+
{
|
|
437
|
+
...Tokens[ChainId.MAINNET]!.COINBASE_WRAPPED_STAKED_ETH,
|
|
438
|
+
extensions: {
|
|
439
|
+
bridgeInfo: {
|
|
440
|
+
[ChainId.ARBITRUM_ONE]: {
|
|
441
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_ARBITRUM_ONE.address,
|
|
442
|
+
},
|
|
443
|
+
[ChainId.BASE_GOERLI]: {
|
|
444
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH_BASE_GOERLI.address,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
...Tokens[ChainId.ARBITRUM_ONE]!.COINBASE_WRAPPED_STAKED_ETH,
|
|
451
|
+
name: 'Coinbase Wrapped Staked ETH',
|
|
452
|
+
extensions: {
|
|
453
|
+
bridgeInfo: {
|
|
454
|
+
[ChainId.MAINNET]: {
|
|
455
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH.address,
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
...Tokens[ChainId.BASE_GOERLI]!.COINBASE_WRAPPED_STAKED_ETH,
|
|
462
|
+
name: 'Coinbase Wrapped Staked ETH',
|
|
463
|
+
extensions: {
|
|
464
|
+
bridgeInfo: {
|
|
465
|
+
[ChainId.MAINNET]: {
|
|
466
|
+
tokenAddress: COINBASE_WRAPPED_STAKED_ETH.address,
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
])
|
|
472
|
+
})
|
|
320
473
|
})
|
package/src/index.ts
CHANGED
|
@@ -25,13 +25,22 @@ export async function chainify(
|
|
|
25
25
|
ChainId.CELO,
|
|
26
26
|
ChainId.BNB,
|
|
27
27
|
ChainId.AVALANCHE,
|
|
28
|
+
ChainId.BASE_GOERLI,
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
const chainified = await chainifyTokenList(l2Chains, l1TokenListOrPathOrUrl)
|
|
31
|
-
|
|
32
|
+
const merged = mergeTokenLists(
|
|
32
33
|
l1TokenList, // providing l1 first to make sure duplicated tokens resolve to this list
|
|
33
34
|
chainified
|
|
34
35
|
)
|
|
36
|
+
|
|
37
|
+
const tokenMap = {}
|
|
38
|
+
if (merged.tokens && merged.tokens.length > 0) {
|
|
39
|
+
merged.tokens.forEach((token) => {
|
|
40
|
+
tokenMap[`${token.chainId}_${token.address}`] = token
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
return { ...merged, tokenMap }
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MappingProvider } from './MappingProvider'
|
|
2
|
+
import { ChainId } from '../constants/chainId'
|
|
3
|
+
import { getTokenList } from '../utils'
|
|
4
|
+
import { GenericMappedTokenData } from '../constants/types'
|
|
5
|
+
|
|
6
|
+
const baseGoerliTokenListURL =
|
|
7
|
+
'https://raw.githubusercontent.com/' +
|
|
8
|
+
'ethereum-optimism/ethereum-optimism.github.io/master/optimism.tokenlist.json'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The Base Goerli mapping (linked above) is manually maintained by the Coinbase team
|
|
12
|
+
* in this repository: https://github.com/ethereum-optimism/ethereum-optimism.github.io.
|
|
13
|
+
*/
|
|
14
|
+
export class BaseGoerliMappingProvider implements MappingProvider {
|
|
15
|
+
async provide(): Promise<GenericMappedTokenData> {
|
|
16
|
+
const tokens: { [key: string]: string | undefined } = {}
|
|
17
|
+
|
|
18
|
+
let allTokens = await getTokenList(baseGoerliTokenListURL)
|
|
19
|
+
|
|
20
|
+
let opTokenId_baseGoerliAddressMap = {}
|
|
21
|
+
allTokens.tokens.forEach((token) => {
|
|
22
|
+
if (token.chainId === ChainId.BASE_GOERLI) {
|
|
23
|
+
if (typeof token.extensions?.opTokenId === 'string') {
|
|
24
|
+
opTokenId_baseGoerliAddressMap[token.extensions.opTokenId] =
|
|
25
|
+
token.address
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
allTokens.tokens.forEach((token) => {
|
|
31
|
+
if (
|
|
32
|
+
token.chainId === ChainId.MAINNET &&
|
|
33
|
+
typeof token.extensions?.opTokenId === 'string' &&
|
|
34
|
+
token.extensions!.opTokenId in opTokenId_baseGoerliAddressMap
|
|
35
|
+
) {
|
|
36
|
+
tokens[token.address.toLowerCase()] =
|
|
37
|
+
opTokenId_baseGoerliAddressMap[token.extensions!.opTokenId]
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return tokens
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/providers/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MappedToken,
|
|
22
22
|
} from '../constants/types'
|
|
23
23
|
import { AvalancheMappingProvider } from './AvalancheMappingProvider'
|
|
24
|
+
import { BaseGoerliMappingProvider } from './BaseGoerliMappingProvider'
|
|
24
25
|
|
|
25
26
|
const web3 = new Web3()
|
|
26
27
|
|
|
@@ -31,6 +32,7 @@ const CHAINS_WITH_MAPPING_PROVIDERS = [
|
|
|
31
32
|
ChainId.OPTIMISM,
|
|
32
33
|
ChainId.BNB,
|
|
33
34
|
ChainId.AVALANCHE,
|
|
35
|
+
ChainId.BASE_GOERLI,
|
|
34
36
|
]
|
|
35
37
|
|
|
36
38
|
export async function buildList(
|
|
@@ -150,6 +152,8 @@ function getMappingProvider(chainId: ChainId, l1TokenList: TokenList) {
|
|
|
150
152
|
return new BnbMappingProvider()
|
|
151
153
|
case ChainId.AVALANCHE:
|
|
152
154
|
return new AvalancheMappingProvider()
|
|
155
|
+
case ChainId.BASE_GOERLI:
|
|
156
|
+
return new BaseGoerliMappingProvider()
|
|
153
157
|
default:
|
|
154
158
|
throw new Error(`Chain ${chainId} not supported for fetching mappings.`)
|
|
155
159
|
}
|
package/src/utils.ts
CHANGED
|
@@ -111,7 +111,9 @@ export function getRpcUrl(chainId: ChainId): string {
|
|
|
111
111
|
case ChainId.BNB:
|
|
112
112
|
return 'https://bsc-dataseed1.binance.org'
|
|
113
113
|
case ChainId.AVALANCHE:
|
|
114
|
-
return 'https://api.avax.network/ext/bc/C/rpc'
|
|
114
|
+
return 'https://api.avax.network/ext/bc/C/rpc'
|
|
115
|
+
case ChainId.BASE_GOERLI:
|
|
116
|
+
return 'https://goerli.base.org'
|
|
115
117
|
default:
|
|
116
118
|
}
|
|
117
119
|
throw new Error('Unsupported ChainId')
|