@uniswap/token-list-bridge-utils 2.10.0 → 2.11.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/excludedTokens.d.ts +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/token-list-bridge-utils.cjs.development.js +35 -5
- 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 +34 -6
- package/dist/token-list-bridge-utils.esm.js.map +1 -1
- package/package.json +6 -2
- package/src/constants/excludedTokens.test.ts +124 -0
- package/src/constants/excludedTokens.ts +25 -0
- package/src/index.test.ts +151 -1
- package/src/index.ts +2 -0
- package/src/providers/index.ts +21 -3
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { isTokenExcluded, EXCLUDED_TOKENS } from './excludedTokens'
|
|
2
|
+
import { ChainId } from './chainId'
|
|
3
|
+
|
|
4
|
+
describe('excludedTokens', () => {
|
|
5
|
+
describe('isTokenExcluded', () => {
|
|
6
|
+
it('returns true for excluded tokens on Arbitrum', () => {
|
|
7
|
+
expect(
|
|
8
|
+
isTokenExcluded(
|
|
9
|
+
ChainId.ARBITRUM_ONE,
|
|
10
|
+
'0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429'
|
|
11
|
+
)
|
|
12
|
+
).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns true for excluded tokens on Polygon', () => {
|
|
16
|
+
expect(
|
|
17
|
+
isTokenExcluded(
|
|
18
|
+
ChainId.POLYGON,
|
|
19
|
+
'0x553d3D295e0f695B9228246232eDF400ed3560B5'
|
|
20
|
+
)
|
|
21
|
+
).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('returns true for excluded tokens on Unichain', () => {
|
|
25
|
+
expect(
|
|
26
|
+
isTokenExcluded(
|
|
27
|
+
ChainId.UNICHAIN,
|
|
28
|
+
'0x89f7C0870794103744C8042630CC1C846a858E57'
|
|
29
|
+
)
|
|
30
|
+
).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('is case-insensitive for token addresses', () => {
|
|
34
|
+
expect(
|
|
35
|
+
isTokenExcluded(
|
|
36
|
+
ChainId.ARBITRUM_ONE,
|
|
37
|
+
'0xFEB4DFC8C4CF7ED305BB08065D08EC6EE6728429'
|
|
38
|
+
)
|
|
39
|
+
).toBe(true)
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
isTokenExcluded(
|
|
43
|
+
ChainId.POLYGON,
|
|
44
|
+
'0X553D3D295E0F695B9228246232EDF400ED3560B5'
|
|
45
|
+
)
|
|
46
|
+
).toBe(true)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('returns false for non-excluded tokens', () => {
|
|
50
|
+
expect(
|
|
51
|
+
isTokenExcluded(
|
|
52
|
+
ChainId.ARBITRUM_ONE,
|
|
53
|
+
'0x0000000000000000000000000000000000000000'
|
|
54
|
+
)
|
|
55
|
+
).toBe(false)
|
|
56
|
+
|
|
57
|
+
expect(
|
|
58
|
+
isTokenExcluded(
|
|
59
|
+
ChainId.POLYGON,
|
|
60
|
+
'0x0000000000000000000000000000000000000000'
|
|
61
|
+
)
|
|
62
|
+
).toBe(false)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('returns false for chains without exclusions', () => {
|
|
66
|
+
expect(
|
|
67
|
+
isTokenExcluded(
|
|
68
|
+
ChainId.OPTIMISM,
|
|
69
|
+
'0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429'
|
|
70
|
+
)
|
|
71
|
+
).toBe(false)
|
|
72
|
+
|
|
73
|
+
expect(
|
|
74
|
+
isTokenExcluded(
|
|
75
|
+
ChainId.BASE,
|
|
76
|
+
'0x553d3D295e0f695B9228246232eDF400ed3560B5'
|
|
77
|
+
)
|
|
78
|
+
).toBe(false)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('returns false when excluded token is checked against wrong chain', () => {
|
|
82
|
+
// Arbitrum excluded token checked on Polygon
|
|
83
|
+
expect(
|
|
84
|
+
isTokenExcluded(
|
|
85
|
+
ChainId.POLYGON,
|
|
86
|
+
'0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429'
|
|
87
|
+
)
|
|
88
|
+
).toBe(false)
|
|
89
|
+
|
|
90
|
+
// Polygon excluded token checked on Arbitrum
|
|
91
|
+
expect(
|
|
92
|
+
isTokenExcluded(
|
|
93
|
+
ChainId.ARBITRUM_ONE,
|
|
94
|
+
'0x553d3D295e0f695B9228246232eDF400ed3560B5'
|
|
95
|
+
)
|
|
96
|
+
).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('EXCLUDED_TOKENS', () => {
|
|
101
|
+
it('contains the correct chains', () => {
|
|
102
|
+
expect(EXCLUDED_TOKENS[ChainId.ARBITRUM_ONE]).toBeDefined()
|
|
103
|
+
expect(EXCLUDED_TOKENS[ChainId.POLYGON]).toBeDefined()
|
|
104
|
+
expect(EXCLUDED_TOKENS[ChainId.UNICHAIN]).toBeDefined()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('stores addresses in lowercase', () => {
|
|
108
|
+
const arbitrumExcluded = Array.from(EXCLUDED_TOKENS[ChainId.ARBITRUM_ONE])
|
|
109
|
+
expect(
|
|
110
|
+
arbitrumExcluded.every((addr) => addr === addr.toLowerCase())
|
|
111
|
+
).toBe(true)
|
|
112
|
+
|
|
113
|
+
const polygonExcluded = Array.from(EXCLUDED_TOKENS[ChainId.POLYGON])
|
|
114
|
+
expect(polygonExcluded.every((addr) => addr === addr.toLowerCase())).toBe(
|
|
115
|
+
true
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const unichainExcluded = Array.from(EXCLUDED_TOKENS[ChainId.UNICHAIN])
|
|
119
|
+
expect(
|
|
120
|
+
unichainExcluded.every((addr) => addr === addr.toLowerCase())
|
|
121
|
+
).toBe(true)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ChainId } from './chainId'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tokens to exclude from being added to the token list.
|
|
5
|
+
* Key: chainId, Value: Set of lowercase token addresses to exclude on that chain
|
|
6
|
+
*/
|
|
7
|
+
export const EXCLUDED_TOKENS: { [chainId: number]: Set<string> } = {
|
|
8
|
+
[ChainId.ARBITRUM_ONE]: new Set([
|
|
9
|
+
'0xfeb4dfc8c4cf7ed305bb08065d08ec6ee6728429',
|
|
10
|
+
]),
|
|
11
|
+
[ChainId.POLYGON]: new Set(['0x553d3d295e0f695b9228246232edf400ed3560b5']),
|
|
12
|
+
[ChainId.UNICHAIN]: new Set(['0x89f7c0870794103744c8042630cc1c846a858e57']),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if a token address should be excluded on a given chain
|
|
17
|
+
*/
|
|
18
|
+
export function isTokenExcluded(
|
|
19
|
+
chainId: number,
|
|
20
|
+
tokenAddress: string
|
|
21
|
+
): boolean {
|
|
22
|
+
const excludedSet = EXCLUDED_TOKENS[chainId]
|
|
23
|
+
if (!excludedSet) return false
|
|
24
|
+
return excludedSet.has(tokenAddress.toLowerCase())
|
|
25
|
+
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TokenInfo } from '@uniswap/token-lists'
|
|
1
|
+
import { TokenInfo, TokenList } from '@uniswap/token-lists'
|
|
2
|
+
import { Token } from '@uniswap/sdk-core'
|
|
2
3
|
import { chainify, chainifyTokenList, mergeTokenLists } from '.'
|
|
3
4
|
import { ChainId } from './constants/chainId'
|
|
4
5
|
import {
|
|
@@ -457,3 +458,152 @@ describe(chainify, () => {
|
|
|
457
458
|
])
|
|
458
459
|
})
|
|
459
460
|
})
|
|
461
|
+
|
|
462
|
+
describe('excluded tokens', () => {
|
|
463
|
+
it('excludes tokens from Arbitrum when specified in EXCLUDED_TOKENS', async () => {
|
|
464
|
+
// Use a token that has the excluded L2 address as its explicit bridgeInfo
|
|
465
|
+
const testTokenList: TokenList = {
|
|
466
|
+
name: 'Test Exclusion',
|
|
467
|
+
timestamp: new Date().toISOString(),
|
|
468
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
469
|
+
tokens: [
|
|
470
|
+
{
|
|
471
|
+
chainId: ChainId.MAINNET,
|
|
472
|
+
address: '0x0000000000000000000000000000000000000001',
|
|
473
|
+
name: 'Test Token',
|
|
474
|
+
symbol: 'TEST',
|
|
475
|
+
decimals: 18,
|
|
476
|
+
extensions: {
|
|
477
|
+
bridgeInfo: {
|
|
478
|
+
[ChainId.ARBITRUM_ONE]: {
|
|
479
|
+
tokenAddress: '0xfEb4DfC8C4Cf7Ed305bb08065D08eC6ee6728429', // excluded arbitrum address
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const chainified = await chainifyTokenList(
|
|
488
|
+
[ChainId.ARBITRUM_ONE],
|
|
489
|
+
testTokenList
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
// Should only contain mainnet token, not the Arbitrum L2 token
|
|
493
|
+
expect(chainified.tokens.length).toBe(1)
|
|
494
|
+
expect(chainified.tokens[0].chainId).toBe(ChainId.MAINNET)
|
|
495
|
+
// Should not have bridgeInfo for Arbitrum since L2 token is excluded
|
|
496
|
+
expect(chainified.tokens[0].extensions?.bridgeInfo).toBeUndefined()
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
it('excludes tokens from Polygon when specified in EXCLUDED_TOKENS', async () => {
|
|
500
|
+
// Use DAI which has a valid Polygon mapping
|
|
501
|
+
const testTokenList: TokenList = {
|
|
502
|
+
name: 'Test Polygon Exclusion',
|
|
503
|
+
timestamp: new Date().toISOString(),
|
|
504
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
505
|
+
tokens: [
|
|
506
|
+
{
|
|
507
|
+
chainId: ChainId.MAINNET,
|
|
508
|
+
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
|
|
509
|
+
name: 'Dai Stablecoin',
|
|
510
|
+
symbol: 'DAI',
|
|
511
|
+
decimals: 18,
|
|
512
|
+
extensions: {
|
|
513
|
+
bridgeInfo: {
|
|
514
|
+
[ChainId.POLYGON]: {
|
|
515
|
+
tokenAddress: '0x553d3D295e0f695B9228246232eDF400ed3560B5', // excluded polygon address
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
],
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const chainified = await chainifyTokenList([ChainId.POLYGON], testTokenList)
|
|
524
|
+
|
|
525
|
+
// Should only contain mainnet token, not the Polygon L2 token
|
|
526
|
+
expect(chainified.tokens.length).toBe(1)
|
|
527
|
+
expect(chainified.tokens[0].chainId).toBe(ChainId.MAINNET)
|
|
528
|
+
// Should not have bridgeInfo for Polygon since the L2 token is excluded
|
|
529
|
+
expect(chainified.tokens[0].extensions?.bridgeInfo).toBeUndefined()
|
|
530
|
+
})
|
|
531
|
+
|
|
532
|
+
it('excludes tokens from Unichain when specified in EXCLUDED_TOKENS', async () => {
|
|
533
|
+
// Create a token list with the excluded Unichain token
|
|
534
|
+
const EXCLUDED_TOKEN_MAINNET = new Token(
|
|
535
|
+
ChainId.MAINNET,
|
|
536
|
+
'0x45804880de22913dafe09f4980848ece6ecbaf78', // PAX Gold - maps to excluded unichain token
|
|
537
|
+
18,
|
|
538
|
+
'PAXG',
|
|
539
|
+
'PAX Gold'
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
const testTokenList: TokenList = {
|
|
543
|
+
name: 'Test Unichain Exclusion',
|
|
544
|
+
timestamp: new Date().toISOString(),
|
|
545
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
546
|
+
tokens: [
|
|
547
|
+
{
|
|
548
|
+
chainId: ChainId.MAINNET,
|
|
549
|
+
address: EXCLUDED_TOKEN_MAINNET.address,
|
|
550
|
+
name: EXCLUDED_TOKEN_MAINNET.name!,
|
|
551
|
+
symbol: EXCLUDED_TOKEN_MAINNET.symbol!,
|
|
552
|
+
decimals: EXCLUDED_TOKEN_MAINNET.decimals,
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const chainified = await chainifyTokenList(
|
|
558
|
+
[ChainId.UNICHAIN],
|
|
559
|
+
testTokenList
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
// Should only contain mainnet token, not the Unichain L2 token
|
|
563
|
+
expect(chainified.tokens.length).toBe(1)
|
|
564
|
+
expect(chainified.tokens[0].chainId).toBe(ChainId.MAINNET)
|
|
565
|
+
expect(chainified.tokens[0].address.toLowerCase()).toBe(
|
|
566
|
+
EXCLUDED_TOKEN_MAINNET.address.toLowerCase()
|
|
567
|
+
)
|
|
568
|
+
// Should not have bridgeInfo for Unichain
|
|
569
|
+
expect(chainified.tokens[0].extensions?.bridgeInfo).toBeUndefined()
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
it('does not exclude tokens on non-excluded chains', async () => {
|
|
573
|
+
// Use the same mainnet address that maps to excluded Arbitrum token
|
|
574
|
+
// but chainify for Optimism instead
|
|
575
|
+
const TOKEN_MAINNET = new Token(
|
|
576
|
+
ChainId.MAINNET,
|
|
577
|
+
'0x8290333cef9e6d528dd5618fb97a76f268f3edd4',
|
|
578
|
+
18,
|
|
579
|
+
'ANKR',
|
|
580
|
+
'Ankr Network'
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
const testTokenList: TokenList = {
|
|
584
|
+
name: 'Test Non-Exclusion',
|
|
585
|
+
timestamp: new Date().toISOString(),
|
|
586
|
+
version: { major: 1, minor: 0, patch: 0 },
|
|
587
|
+
tokens: [
|
|
588
|
+
{
|
|
589
|
+
chainId: ChainId.MAINNET,
|
|
590
|
+
address: TOKEN_MAINNET.address,
|
|
591
|
+
name: TOKEN_MAINNET.name!,
|
|
592
|
+
symbol: TOKEN_MAINNET.symbol!,
|
|
593
|
+
decimals: TOKEN_MAINNET.decimals,
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const chainified = await chainifyTokenList(
|
|
599
|
+
[ChainId.OPTIMISM],
|
|
600
|
+
testTokenList
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
// May contain L2 tokens if valid mappings exist for Optimism
|
|
604
|
+
const mainnetToken = chainified.tokens.find(
|
|
605
|
+
(t) => t.chainId === ChainId.MAINNET
|
|
606
|
+
)
|
|
607
|
+
expect(mainnetToken).toBeDefined()
|
|
608
|
+
})
|
|
609
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from './utils'
|
|
10
10
|
import { verifyExtensions } from './verify'
|
|
11
11
|
|
|
12
|
+
export { EXCLUDED_TOKENS, isTokenExcluded } from './constants/excludedTokens'
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Adds bridgeInfo to the given token list for Optimism, Polygon and Arbitrum.
|
|
14
16
|
* @param l1TokenListOrPathOrUrl
|
package/src/providers/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { AvalancheMappingProvider } from './AvalancheMappingProvider'
|
|
|
25
25
|
import { BaseMappingProvider } from './BaseMappingProvider'
|
|
26
26
|
import { CeloMappingProvider } from './CeloMappingProvider'
|
|
27
27
|
import { SoneiumMappingProvider } from './SoneiumMappingProvider'
|
|
28
|
+
import { isTokenExcluded } from '../constants/excludedTokens'
|
|
28
29
|
|
|
29
30
|
const web3 = new Web3()
|
|
30
31
|
|
|
@@ -72,10 +73,17 @@ export async function buildList(
|
|
|
72
73
|
chainId,
|
|
73
74
|
chainIdToMappingsMap
|
|
74
75
|
)
|
|
75
|
-
|
|
76
|
+
const childTokenAddress =
|
|
77
|
+
chainIdToChildTokenDetailsMap[chainId].childTokenAddress
|
|
78
|
+
|
|
79
|
+
// Only add bridgeInfo if child token is valid and not excluded
|
|
80
|
+
if (
|
|
81
|
+
chainIdToChildTokenDetailsMap[chainId].childTokenValid &&
|
|
82
|
+
childTokenAddress &&
|
|
83
|
+
!isTokenExcluded(chainId, childTokenAddress)
|
|
84
|
+
) {
|
|
76
85
|
l2MappingExtension.extensions.bridgeInfo[chainId] = {
|
|
77
|
-
tokenAddress:
|
|
78
|
-
chainIdToChildTokenDetailsMap[chainId].childTokenAddress,
|
|
86
|
+
tokenAddress: childTokenAddress,
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
})
|
|
@@ -83,6 +91,16 @@ export async function buildList(
|
|
|
83
91
|
|
|
84
92
|
// build the TokenInfo objects with bridgeInfo extension
|
|
85
93
|
l2ChainIds.concat([ChainId.MAINNET]).forEach((chainId) => {
|
|
94
|
+
const tokenAddress =
|
|
95
|
+
chainId === ChainId.MAINNET
|
|
96
|
+
? l1Token.address
|
|
97
|
+
: chainIdToChildTokenDetailsMap[chainId]?.childTokenAddress
|
|
98
|
+
|
|
99
|
+
// Skip if token is excluded on this chain
|
|
100
|
+
if (tokenAddress && isTokenExcluded(chainId, tokenAddress)) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
86
104
|
if (
|
|
87
105
|
chainId === ChainId.MAINNET ||
|
|
88
106
|
chainIdToChildTokenDetailsMap[chainId].childTokenValid
|