@uniswap/token-list-bridge-utils 1.0.5 → 1.0.8

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.
@@ -0,0 +1,226 @@
1
+ import { AbiItem } from 'web3-utils'
2
+
3
+ const abi: AbiItem[] = [
4
+ {
5
+ constant: true,
6
+ inputs: [],
7
+ name: 'name',
8
+ outputs: [
9
+ {
10
+ name: '',
11
+ type: 'string',
12
+ },
13
+ ],
14
+ payable: false,
15
+ stateMutability: 'view',
16
+ type: 'function',
17
+ },
18
+ {
19
+ constant: false,
20
+ inputs: [
21
+ {
22
+ name: '_spender',
23
+ type: 'address',
24
+ },
25
+ {
26
+ name: '_value',
27
+ type: 'uint256',
28
+ },
29
+ ],
30
+ name: 'approve',
31
+ outputs: [
32
+ {
33
+ name: '',
34
+ type: 'bool',
35
+ },
36
+ ],
37
+ payable: false,
38
+ stateMutability: 'nonpayable',
39
+ type: 'function',
40
+ },
41
+ {
42
+ constant: true,
43
+ inputs: [],
44
+ name: 'totalSupply',
45
+ outputs: [
46
+ {
47
+ name: '',
48
+ type: 'uint256',
49
+ },
50
+ ],
51
+ payable: false,
52
+ stateMutability: 'view',
53
+ type: 'function',
54
+ },
55
+ {
56
+ constant: false,
57
+ inputs: [
58
+ {
59
+ name: '_from',
60
+ type: 'address',
61
+ },
62
+ {
63
+ name: '_to',
64
+ type: 'address',
65
+ },
66
+ {
67
+ name: '_value',
68
+ type: 'uint256',
69
+ },
70
+ ],
71
+ name: 'transferFrom',
72
+ outputs: [
73
+ {
74
+ name: '',
75
+ type: 'bool',
76
+ },
77
+ ],
78
+ payable: false,
79
+ stateMutability: 'nonpayable',
80
+ type: 'function',
81
+ },
82
+ {
83
+ constant: true,
84
+ inputs: [],
85
+ name: 'decimals',
86
+ outputs: [
87
+ {
88
+ name: '',
89
+ type: 'uint8',
90
+ },
91
+ ],
92
+ payable: false,
93
+ stateMutability: 'view',
94
+ type: 'function',
95
+ },
96
+ {
97
+ constant: true,
98
+ inputs: [
99
+ {
100
+ name: '_owner',
101
+ type: 'address',
102
+ },
103
+ ],
104
+ name: 'balanceOf',
105
+ outputs: [
106
+ {
107
+ name: 'balance',
108
+ type: 'uint256',
109
+ },
110
+ ],
111
+ payable: false,
112
+ stateMutability: 'view',
113
+ type: 'function',
114
+ },
115
+ {
116
+ constant: true,
117
+ inputs: [],
118
+ name: 'symbol',
119
+ outputs: [
120
+ {
121
+ name: '',
122
+ type: 'string',
123
+ },
124
+ ],
125
+ payable: false,
126
+ stateMutability: 'view',
127
+ type: 'function',
128
+ },
129
+ {
130
+ constant: false,
131
+ inputs: [
132
+ {
133
+ name: '_to',
134
+ type: 'address',
135
+ },
136
+ {
137
+ name: '_value',
138
+ type: 'uint256',
139
+ },
140
+ ],
141
+ name: 'transfer',
142
+ outputs: [
143
+ {
144
+ name: '',
145
+ type: 'bool',
146
+ },
147
+ ],
148
+ payable: false,
149
+ stateMutability: 'nonpayable',
150
+ type: 'function',
151
+ },
152
+ {
153
+ constant: true,
154
+ inputs: [
155
+ {
156
+ name: '_owner',
157
+ type: 'address',
158
+ },
159
+ {
160
+ name: '_spender',
161
+ type: 'address',
162
+ },
163
+ ],
164
+ name: 'allowance',
165
+ outputs: [
166
+ {
167
+ name: '',
168
+ type: 'uint256',
169
+ },
170
+ ],
171
+ payable: false,
172
+ stateMutability: 'view',
173
+ type: 'function',
174
+ },
175
+ {
176
+ payable: true,
177
+ stateMutability: 'payable',
178
+ type: 'fallback',
179
+ },
180
+ {
181
+ anonymous: false,
182
+ inputs: [
183
+ {
184
+ indexed: true,
185
+ name: 'owner',
186
+ type: 'address',
187
+ },
188
+ {
189
+ indexed: true,
190
+ name: 'spender',
191
+ type: 'address',
192
+ },
193
+ {
194
+ indexed: false,
195
+ name: 'value',
196
+ type: 'uint256',
197
+ },
198
+ ],
199
+ name: 'Approval',
200
+ type: 'event',
201
+ },
202
+ {
203
+ anonymous: false,
204
+ inputs: [
205
+ {
206
+ indexed: true,
207
+ name: 'from',
208
+ type: 'address',
209
+ },
210
+ {
211
+ indexed: true,
212
+ name: 'to',
213
+ type: 'address',
214
+ },
215
+ {
216
+ indexed: false,
217
+ name: 'value',
218
+ type: 'uint256',
219
+ },
220
+ ],
221
+ name: 'Transfer',
222
+ type: 'event',
223
+ },
224
+ ]
225
+
226
+ export default abi
@@ -1,6 +1,7 @@
1
1
  import { config } from 'dotenv'
2
+ import { ChainId } from '../constants/chainId'
3
+ import { getRpcUrl } from '../utils'
2
4
 
3
- // fall back to https://cloudflare-eth.com/ if MAINNET_RPC not provided in an .env file
5
+ // MAINNET_RPC value is required for arb-sdk. fall back to https://cloudflare-eth.com/ if MAINNET_RPC not provided in an .env file
4
6
  config()
5
- process.env.MAINNET_RPC =
6
- process.env.MAINNET_RPC || 'https://cloudflare-eth.com/'
7
+ process.env.MAINNET_RPC ??= getRpcUrl(ChainId.MAINNET)
package/src/index.test.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { TokenInfo } from '@uniswap/token-lists'
2
2
  import { chainify, chainifyTokenList, mergeTokenLists } from '.'
3
3
  import { ChainId } from './constants/chainId'
4
- import { ArbitrumMappingProvider } from './providers/ArbitrumMappingProvider'
5
4
  import {
6
5
  DAI,
7
6
  DAI_ARBITRUM_ONE,
@@ -237,4 +236,4 @@ describe(chainify, () => {
237
236
  },
238
237
  ])
239
238
  })
240
- })
239
+ })
@@ -1,15 +1,26 @@
1
1
  import { ChainId } from '../constants/chainId'
2
+ import { Contract } from 'web3-eth-contract'
2
3
  import { ArbitrumMappingProvider } from './ArbitrumMappingProvider'
3
4
  import { OptimismMappingProvider } from './OptimismMappingProvider'
4
5
  import { PolygonMappingProvider } from './PolygonMappingProvider'
5
6
  import { TokenInfo, TokenList } from '@uniswap/token-lists'
6
- import { compareTokenInfos } from '../utils'
7
7
  import { ethers } from 'ethers'
8
+ import {
9
+ compareTokenInfos,
10
+ getRpcUrl,
11
+ getTokenSymbolFromContract,
12
+ } from '../utils'
13
+ import ERC20Abi from '../abis/erc20'
14
+ // TODO: use ethers for contract calls (without performance reduction).
15
+ import Web3 from 'web3'
16
+
17
+ const web3 = new Web3()
8
18
 
9
19
  export async function buildList(
10
20
  chainId: ChainId,
11
21
  l1TokenList: TokenList
12
22
  ): Promise<TokenList> {
23
+ web3.setProvider(getRpcUrl(chainId))
13
24
  const tokenAddressMap = await getMappingProvider(
14
25
  chainId,
15
26
  l1TokenList
@@ -17,9 +28,15 @@ export async function buildList(
17
28
  const mappedTokens = []
18
29
  for (const rootToken of l1TokenList.tokens) {
19
30
  const childToken = tokenAddressMap[rootToken.address.toLowerCase()]
31
+ const childTokenAddress = childToken
32
+ ? ethers.utils.getAddress(
33
+ typeof childToken === 'object' ? childToken.child_token : childToken
34
+ )
35
+ : undefined
20
36
  const childTokenValid = Boolean(
21
- childToken &&
22
- (typeof childToken === 'object' ? !childToken.deleted : true)
37
+ childTokenAddress &&
38
+ (typeof childToken === 'object' ? !childToken.deleted : true) &&
39
+ (await hasExistingTokenContract(childTokenAddress, chainId))
23
40
  )
24
41
 
25
42
  if (rootToken.chainId === ChainId.MAINNET) {
@@ -41,11 +58,7 @@ export async function buildList(
41
58
  extensions: {
42
59
  bridgeInfo: {
43
60
  [chainId]: {
44
- tokenAddress: ethers.utils.getAddress(
45
- typeof childToken === 'object'
46
- ? childToken.child_token
47
- : childToken!
48
- ),
61
+ tokenAddress: childTokenAddress,
49
62
  },
50
63
  },
51
64
  },
@@ -61,11 +74,7 @@ export async function buildList(
61
74
  const childTokenInfo: TokenInfo = {
62
75
  ...rootToken,
63
76
  chainId: chainId,
64
- address: ethers.utils.getAddress(
65
- typeof childToken === 'object'
66
- ? childToken.child_token
67
- : childToken!
68
- ),
77
+ address: childTokenAddress,
69
78
  ...toRootExtensions,
70
79
  } as unknown as TokenInfo
71
80
  mappedTokens.push(childTokenInfo)
@@ -83,6 +92,17 @@ export async function buildList(
83
92
  return tokenList
84
93
  }
85
94
 
95
+ // using a symbol lookup contract call to check whether the token exists on the L2
96
+ async function hasExistingTokenContract(address: string, chainId: ChainId) {
97
+ try {
98
+ const contract: Contract = new web3.eth.Contract(ERC20Abi, address)
99
+ await getTokenSymbolFromContract(contract)
100
+ } catch {
101
+ return false
102
+ }
103
+ return true
104
+ }
105
+
86
106
  function getMappingProvider(chainId: ChainId, l1TokenList: TokenList) {
87
107
  switch (chainId) {
88
108
  case ChainId.ARBITRUM_ONE:
package/src/utils.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { TokenInfo, TokenList } from '@uniswap/token-lists'
2
2
  import { readFileSync, existsSync } from 'fs'
3
3
  import axios from 'axios'
4
+ import { ChainId } from './constants/chainId'
5
+ import { Contract } from 'web3-eth-contract'
4
6
 
5
7
  export type TokenListOrFetchableTokenList = TokenList | string
6
8
 
@@ -89,3 +91,29 @@ export const isTokenList = (obj: any) => {
89
91
  throw new Error('tokenlist typeguard error: token missing required key')
90
92
  }
91
93
  }
94
+
95
+ export function getRpcUrl(chainId: ChainId): string {
96
+ switch (chainId) {
97
+ case ChainId.MAINNET:
98
+ return 'https://cloudflare-eth.com/'
99
+ case ChainId.OPTIMISM:
100
+ return 'https://mainnet.optimism.io'
101
+ case ChainId.OPTIMISTIC_KOVAN:
102
+ return 'https://kovan.optimism.io'
103
+ case ChainId.ARBITRUM_ONE:
104
+ return 'https://arb1.arbitrum.io/rpc'
105
+ case ChainId.ARBITRUM_RINKEBY:
106
+ return 'https://rinkeby.arbitrum.io/rpc'
107
+ case ChainId.POLYGON:
108
+ return 'https://polygon-rpc.com/'
109
+ case ChainId.POLYGON_MUMBAI:
110
+ return 'https://rpc-endpoints.superfluid.dev/mumbai'
111
+ default:
112
+ }
113
+ throw new Error('Unsupported ChainId')
114
+ }
115
+
116
+ export async function getTokenSymbolFromContract(tokenContract: Contract) {
117
+ const symbol = await Promise.all([tokenContract.methods.symbol().call()])
118
+ return symbol
119
+ }