@eth-optimism/tokenlist 4.3.1

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.
Files changed (228) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +2 -0
  3. package/.eslintrc.js +188 -0
  4. package/.github/CODEOWNERS +1 -0
  5. package/.github/mergify.yml +57 -0
  6. package/.github/workflows/dependabot.yml +13 -0
  7. package/.github/workflows/generate.yml +57 -0
  8. package/.github/workflows/publish.yml +50 -0
  9. package/.github/workflows/stale.yml +19 -0
  10. package/.github/workflows/test.yml +43 -0
  11. package/.github/workflows/validate.yml +67 -0
  12. package/.prettierrc.js +8 -0
  13. package/.vscode/extensions.json +10 -0
  14. package/.vscode/settings.json +10 -0
  15. package/CNAME +1 -0
  16. package/Dockerfile.bot +14 -0
  17. package/README.md +162 -0
  18. package/babel.config.js +6 -0
  19. package/bin/cli.ts +61 -0
  20. package/data/0xBTC/data.json +19 -0
  21. package/data/0xBTC/logo.png +0 -0
  22. package/data/AAVE/data.json +19 -0
  23. package/data/AAVE/logo.svg +1 -0
  24. package/data/ACX/data.json +15 -0
  25. package/data/ACX/logo.png +0 -0
  26. package/data/AELIN/data.json +11 -0
  27. package/data/AELIN/logo.png +0 -0
  28. package/data/BAL/data.json +19 -0
  29. package/data/BAL/logo.png +0 -0
  30. package/data/BANK/data.json +15 -0
  31. package/data/BANK/logo.png +0 -0
  32. package/data/BAXA/data.json +22 -0
  33. package/data/BAXA/logo.svg +3 -0
  34. package/data/BOB/data.json +16 -0
  35. package/data/BOB/logo.svg +11 -0
  36. package/data/BOND/data.json +19 -0
  37. package/data/BOND/logo.svg +10 -0
  38. package/data/BitANT/data.json +19 -0
  39. package/data/BitANT/logo.png +0 -0
  40. package/data/BitBTC/data.json +31 -0
  41. package/data/BitBTC/logo.png +0 -0
  42. package/data/CRV/data.json +13 -0
  43. package/data/CRV/logo.png +0 -0
  44. package/data/CTSI/data.json +19 -0
  45. package/data/CTSI/logo.svg +23 -0
  46. package/data/DAI/data.json +43 -0
  47. package/data/DAI/logo.svg +13 -0
  48. package/data/DCN/data.json +25 -0
  49. package/data/DCN/logo.svg +47 -0
  50. package/data/DF/data.json +20 -0
  51. package/data/DF/logo.svg +15 -0
  52. package/data/DHT/data.json +13 -0
  53. package/data/DHT/logo.svg +1 -0
  54. package/data/DOLA/data.json +19 -0
  55. package/data/DOLA/logo.png +0 -0
  56. package/data/DUCK/data.json +13 -0
  57. package/data/DUCK/logo.png +0 -0
  58. package/data/ENS/data.json +13 -0
  59. package/data/ENS/logo.png +0 -0
  60. package/data/EQZ/data.json +19 -0
  61. package/data/EQZ/logo.png +0 -0
  62. package/data/EST/data.json +13 -0
  63. package/data/EST/logo.png +0 -0
  64. package/data/ETH/data.json +25 -0
  65. package/data/ETH/logo.svg +13 -0
  66. package/data/EURT/data.json +13 -0
  67. package/data/EURT/logo.png +0 -0
  68. package/data/FLASH/data.json +16 -0
  69. package/data/FLASH/logo.svg +22 -0
  70. package/data/GTC/data.json +19 -0
  71. package/data/GTC/logo.svg +5 -0
  72. package/data/GYSR/data.json +19 -0
  73. package/data/GYSR/logo.png +0 -0
  74. package/data/HAN/data.json +22 -0
  75. package/data/HAN/logo.svg +25 -0
  76. package/data/IBEX/data.json +16 -0
  77. package/data/IBEX/logo.png +0 -0
  78. package/data/INSURE/data.json +16 -0
  79. package/data/INSURE/logo.png +0 -0
  80. package/data/KNC/data.json +19 -0
  81. package/data/KNC/logo.png +0 -0
  82. package/data/KROM/data.json +19 -0
  83. package/data/KROM/logo.png +0 -0
  84. package/data/LAND/data.json +13 -0
  85. package/data/LAND/logo.png +0 -0
  86. package/data/LDO/data.json +30 -0
  87. package/data/LDO/logo.svg +5 -0
  88. package/data/LINK/data.json +25 -0
  89. package/data/LINK/logo.png +0 -0
  90. package/data/LIZ/data.json +19 -0
  91. package/data/LIZ/logo.png +0 -0
  92. package/data/LPF/data.json +15 -0
  93. package/data/LPF/logo.svg +18 -0
  94. package/data/LRC/data.json +13 -0
  95. package/data/LRC/logo.png +0 -0
  96. package/data/LUSD/data.json +13 -0
  97. package/data/LUSD/logo.svg +1 -0
  98. package/data/LYRA/data.json +13 -0
  99. package/data/LYRA/logo.png +0 -0
  100. package/data/MASK/data.json +13 -0
  101. package/data/MASK/logo.svg +4 -0
  102. package/data/MKR/data.json +14 -0
  103. package/data/MKR/logo.png +0 -0
  104. package/data/MOCHI/data.json +16 -0
  105. package/data/MOCHI/logo.svg +36 -0
  106. package/data/MOM/data.json +19 -0
  107. package/data/MOM/logo.svg +5 -0
  108. package/data/OP/data.json +14 -0
  109. package/data/OP/logo.png +0 -0
  110. package/data/OUTb/data.json +13 -0
  111. package/data/OUTb/logo.svg +4 -0
  112. package/data/PAPER/data.json +13 -0
  113. package/data/PAPER/logo.svg +4 -0
  114. package/data/PERP/data.json +25 -0
  115. package/data/PERP/logo.png +0 -0
  116. package/data/PHTK/data.json +16 -0
  117. package/data/PHTK/logo.svg +14 -0
  118. package/data/POOL/data.json +13 -0
  119. package/data/POOL/logo.svg +22 -0
  120. package/data/POP/data.json +16 -0
  121. package/data/POP/logo.svg +8 -0
  122. package/data/PREMIA/data.json +13 -0
  123. package/data/PREMIA/logo.svg +4 -0
  124. package/data/RAI/data.json +19 -0
  125. package/data/RAI/logo.svg +273 -0
  126. package/data/RFWSTETH/data.json +21 -0
  127. package/data/RFWSTETH/logo.svg +36 -0
  128. package/data/RGT/data.json +13 -0
  129. package/data/RGT/logo.png +0 -0
  130. package/data/SARCO/data.json +13 -0
  131. package/data/SARCO/logo.png +0 -0
  132. package/data/SDL/data.json +22 -0
  133. package/data/SDL/logo.svg +5 -0
  134. package/data/SNX/data.json +43 -0
  135. package/data/SNX/logo.svg +20 -0
  136. package/data/SPANK/data.json +19 -0
  137. package/data/SPANK/logo.png +0 -0
  138. package/data/SUKU/data.json +22 -0
  139. package/data/SUKU/logo.png +0 -0
  140. package/data/THALES/data.json +13 -0
  141. package/data/THALES/logo.png +0 -0
  142. package/data/TRB/data.json +22 -0
  143. package/data/TRB/logo.png +0 -0
  144. package/data/TUSD/data.json +19 -0
  145. package/data/TUSD/logo.png +0 -0
  146. package/data/TheDAO/data.json +32 -0
  147. package/data/TheDAO/logo.svg +1 -0
  148. package/data/UBI/data.json +22 -0
  149. package/data/UBI/logo.svg +5 -0
  150. package/data/UMA/data.json +13 -0
  151. package/data/UMA/logo.png +0 -0
  152. package/data/UNI/data.json +19 -0
  153. package/data/UNI/logo.png +0 -0
  154. package/data/USDC/data.json +28 -0
  155. package/data/USDC/logo.png +0 -0
  156. package/data/USDD/data.json +19 -0
  157. package/data/USDD/logo.png +0 -0
  158. package/data/USDT/data.json +28 -0
  159. package/data/USDT/logo.png +0 -0
  160. package/data/USDplus/data.json +14 -0
  161. package/data/USDplus/logo.svg +9 -0
  162. package/data/UST/data.json +13 -0
  163. package/data/UST/logo.png +0 -0
  164. package/data/USX/data.json +31 -0
  165. package/data/USX/logo.svg +14 -0
  166. package/data/VALX/data.json +15 -0
  167. package/data/VALX/logo.png +0 -0
  168. package/data/VELO/data.json +10 -0
  169. package/data/VELO/logo.svg +71 -0
  170. package/data/WAD/data.json +19 -0
  171. package/data/WAD/logo.png +0 -0
  172. package/data/WBTC/data.json +30 -0
  173. package/data/WBTC/logo.svg +20 -0
  174. package/data/WETH/data.json +11 -0
  175. package/data/WETH/logo.png +0 -0
  176. package/data/WMP/data.json +15 -0
  177. package/data/WMP/logo.svg +52 -0
  178. package/data/WOO/data.json +16 -0
  179. package/data/WOO/logo.svg +1489 -0
  180. package/data/XCHF/data.json +13 -0
  181. package/data/XCHF/logo.png +0 -0
  182. package/data/ZRX/data.json +13 -0
  183. package/data/ZRX/logo.png +0 -0
  184. package/data/aCRV/data.json +22 -0
  185. package/data/aCRV/logo.svg +9 -0
  186. package/data/alETH/data.json +16 -0
  187. package/data/alETH/logo.svg +17 -0
  188. package/data/rETH/data.json +13 -0
  189. package/data/rETH/logo.png +0 -0
  190. package/data/sBTC/data.json +11 -0
  191. package/data/sBTC/logo.svg +18 -0
  192. package/data/sETH/data.json +11 -0
  193. package/data/sETH/logo.svg +23 -0
  194. package/data/sLINK/data.json +11 -0
  195. package/data/sLINK/logo.svg +23 -0
  196. package/data/sUSD/data.json +17 -0
  197. package/data/sUSD/logo.svg +17 -0
  198. package/data/veKWENTA/data.json +22 -0
  199. package/data/veKWENTA/logo.svg +24 -0
  200. package/data/wstETH/data.json +31 -0
  201. package/data/wstETH/logo.svg +11 -0
  202. package/dist/declarations/src/index.d.ts +79 -0
  203. package/dist/eth-optimism-tokenlist.cjs.d.ts +1 -0
  204. package/dist/eth-optimism-tokenlist.cjs.dev.js +3098 -0
  205. package/dist/eth-optimism-tokenlist.cjs.js +7 -0
  206. package/dist/eth-optimism-tokenlist.cjs.prod.js +3098 -0
  207. package/dist/eth-optimism-tokenlist.esm.js +3090 -0
  208. package/jest.config.js +5 -0
  209. package/optimism.defaulttokens.json +26 -0
  210. package/optimism.svg +4 -0
  211. package/optimism.tokenlist.json +3086 -0
  212. package/package.json +80 -0
  213. package/src/abi.ts +77 -0
  214. package/src/bot.ts +236 -0
  215. package/src/chains.ts +58 -0
  216. package/src/generate.ts +69 -0
  217. package/src/index.ts +71 -0
  218. package/src/schemas.ts +174 -0
  219. package/src/types.ts +33 -0
  220. package/src/validate.ts +271 -0
  221. package/tests/__snapshots__/generate.test.ts.snap +87 -0
  222. package/tests/data/ETH/data.json +21 -0
  223. package/tests/data/ETH/logo.svg +13 -0
  224. package/tests/data/OP/data.json +16 -0
  225. package/tests/data/OP/logo.png +0 -0
  226. package/tests/defaultTokens.test.ts +12 -0
  227. package/tests/generate.test.ts +20 -0
  228. package/tsconfig.json +27 -0
package/src/schemas.ts ADDED
@@ -0,0 +1,174 @@
1
+ export const ADDRESS_TYPE = {
2
+ type: 'string',
3
+ minLength: 42,
4
+ maxLength: 42,
5
+ }
6
+
7
+ export const TOKEN_SCHEMA = {
8
+ type: 'object',
9
+ properties: {
10
+ address: ADDRESS_TYPE,
11
+ overrides: {
12
+ bridge: ADDRESS_TYPE,
13
+ name: {
14
+ type: 'string',
15
+ },
16
+ symbol: {
17
+ type: 'string',
18
+ },
19
+ decimals: {
20
+ type: 'integer',
21
+ },
22
+ },
23
+ },
24
+ additionalProperties: false,
25
+ required: ['address'],
26
+ }
27
+
28
+ export const TOKEN_DATA_SCHEMA = {
29
+ type: 'object',
30
+ properties: {
31
+ nonstandard: {
32
+ type: 'boolean',
33
+ },
34
+ nobridge: {
35
+ type: 'boolean',
36
+ },
37
+ name: {
38
+ type: 'string',
39
+ },
40
+ symbol: {
41
+ type: 'string',
42
+ },
43
+ decimals: {
44
+ type: 'integer',
45
+ },
46
+ description: {
47
+ type: 'string',
48
+ minLength: 1,
49
+ maxLength: 150,
50
+ },
51
+ website: {
52
+ type: 'string',
53
+ format: 'uri',
54
+ },
55
+ twitter: {
56
+ type: 'string',
57
+ },
58
+ tokens: {
59
+ oneOf: [
60
+ {
61
+ type: 'object',
62
+ properties: {
63
+ ethereum: TOKEN_SCHEMA,
64
+ optimism: TOKEN_SCHEMA,
65
+ kovan: TOKEN_SCHEMA,
66
+ 'optimism-kovan': TOKEN_SCHEMA,
67
+ },
68
+ additionalProperties: false,
69
+ required: ['ethereum', 'optimism', 'kovan', 'optimism-kovan'],
70
+ },
71
+ {
72
+ type: 'object',
73
+ properties: {
74
+ ethereum: TOKEN_SCHEMA,
75
+ optimism: TOKEN_SCHEMA,
76
+ goerli: TOKEN_SCHEMA,
77
+ 'optimism-goerli': TOKEN_SCHEMA,
78
+ },
79
+ additionalProperties: false,
80
+ required: ['ethereum', 'optimism', 'goerli', 'optimism-goerli'],
81
+ },
82
+ {
83
+ type: 'object',
84
+ properties: {
85
+ ethereum: TOKEN_SCHEMA,
86
+ optimism: TOKEN_SCHEMA,
87
+ kovan: TOKEN_SCHEMA,
88
+ 'optimism-kovan': TOKEN_SCHEMA,
89
+ goerli: TOKEN_SCHEMA,
90
+ 'optimism-goerli': TOKEN_SCHEMA,
91
+ },
92
+ additionalProperties: false,
93
+ required: [
94
+ 'ethereum',
95
+ 'optimism',
96
+ 'kovan',
97
+ 'optimism-kovan',
98
+ 'goerli',
99
+ 'optimism-goerli',
100
+ ],
101
+ },
102
+ {
103
+ type: 'object',
104
+ properties: {
105
+ optimism: TOKEN_SCHEMA,
106
+ 'optimism-kovan': TOKEN_SCHEMA,
107
+ },
108
+ additionalProperties: false,
109
+ required: ['optimism', 'optimism-kovan'],
110
+ },
111
+ {
112
+ type: 'object',
113
+ properties: {
114
+ optimism: TOKEN_SCHEMA,
115
+ 'optimism-goerli': TOKEN_SCHEMA,
116
+ },
117
+ additionalProperties: false,
118
+ required: ['optimism', 'optimism-goerli'],
119
+ },
120
+ {
121
+ type: 'object',
122
+ properties: {
123
+ optimism: TOKEN_SCHEMA,
124
+ 'optimism-kovan': TOKEN_SCHEMA,
125
+ 'optimism-goerli': TOKEN_SCHEMA,
126
+ },
127
+ additionalProperties: false,
128
+ required: ['optimism', 'optimism-kovan', 'optimism-goerli'],
129
+ },
130
+ {
131
+ type: 'object',
132
+ properties: {
133
+ ethereum: TOKEN_SCHEMA,
134
+ optimism: TOKEN_SCHEMA,
135
+ },
136
+ additionalProperties: false,
137
+ required: ['ethereum', 'optimism'],
138
+ },
139
+ {
140
+ type: 'object',
141
+ properties: {
142
+ kovan: TOKEN_SCHEMA,
143
+ 'optimism-kovan': TOKEN_SCHEMA,
144
+ },
145
+ additionalProperties: false,
146
+ required: ['kovan', 'optimism-kovan'],
147
+ },
148
+ {
149
+ type: 'object',
150
+ properties: {
151
+ goerli: TOKEN_SCHEMA,
152
+ 'optimism-goerli': TOKEN_SCHEMA,
153
+ },
154
+ additionalProperties: false,
155
+ required: ['goerli', 'optimism-goerli'],
156
+ },
157
+ {
158
+ type: 'object',
159
+ properties: {
160
+ optimism: TOKEN_SCHEMA,
161
+ },
162
+ additionalProperties: false,
163
+ required: ['optimism'],
164
+ },
165
+ ],
166
+ },
167
+ },
168
+ additionalProperties: false,
169
+ required: ['name', 'symbol', 'decimals', 'tokens', 'description', 'website'],
170
+ }
171
+
172
+ module.exports = {
173
+ TOKEN_DATA_SCHEMA,
174
+ }
package/src/types.ts ADDED
@@ -0,0 +1,33 @@
1
+ export interface Token {
2
+ address: string
3
+ overrides?: {
4
+ bridge?: string
5
+ name?: string
6
+ symbol?: string
7
+ decimals?: number
8
+ }
9
+ }
10
+
11
+ export interface TokenData {
12
+ nonstandard?: boolean
13
+ nobridge?: boolean
14
+ twitter?: string
15
+ name: string
16
+ symbol: string
17
+ decimals: number
18
+ description: string
19
+ website: string
20
+ tokens: {
21
+ ethereum?: Token
22
+ optimism?: Token
23
+ kovan?: Token
24
+ 'optimism-kovan'?: Token
25
+ goerli?: Token
26
+ 'optimism-goerli'?: Token
27
+ }
28
+ }
29
+
30
+ export interface ValidationResult {
31
+ type: 'error' | 'warning'
32
+ message: string
33
+ }
@@ -0,0 +1,271 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { URLSearchParams } from 'url'
4
+
5
+ import { glob } from 'glob'
6
+ import fetch from 'node-fetch'
7
+ import { Validator } from 'jsonschema'
8
+ import Ajv from 'ajv'
9
+ import addFormats from 'ajv-formats'
10
+ import { schema } from '@uniswap/token-lists'
11
+ import { ethers } from 'ethers'
12
+ import { sleep } from '@eth-optimism/core-utils'
13
+
14
+ import { generate } from './generate'
15
+ import { TOKEN_DATA_SCHEMA } from './schemas'
16
+ import { NETWORK_DATA } from './chains'
17
+ import { FACTORY_ABI, TOKEN_ABI } from './abi'
18
+ import { TokenData, ValidationResult } from './types'
19
+
20
+ /**
21
+ * Validates a token list data folder.
22
+ *
23
+ * @param datadir Directory containing data files.
24
+ * @param tokens List of tokens to run validation on.
25
+ *
26
+ * @return Validation results.
27
+ */
28
+ export const validate = async (
29
+ datadir: string,
30
+ tokens: string[]
31
+ ): Promise<ValidationResult[]> => {
32
+ // Load data files to validate and filter for requested tokens
33
+ console.log(tokens)
34
+ const folders = fs
35
+ .readdirSync(datadir)
36
+ .sort((a, b) => {
37
+ return a.toLowerCase().localeCompare(b.toLowerCase())
38
+ })
39
+ .filter((folder) => {
40
+ return !tokens || tokens.includes(folder)
41
+ })
42
+
43
+ // Load the CoinGecko tokenlist once to avoid additional requests
44
+ const cgret = await fetch('https://tokens.coingecko.com/uniswap/all.json')
45
+ const cg = await cgret.json()
46
+
47
+ const results = []
48
+ for (const folder of folders) {
49
+ // Make sure the data file exists
50
+ const datafile = path.join(datadir, folder, 'data.json')
51
+ if (!fs.existsSync(datafile)) {
52
+ results.push({
53
+ type: 'error',
54
+ message: `data file ${datafile} does not exist`,
55
+ })
56
+ }
57
+
58
+ // Load the data now that we know it exists
59
+ const data: TokenData = JSON.parse(fs.readFileSync(datafile, 'utf8'))
60
+
61
+ // Make sure ONE logo file exists
62
+ const logofiles = glob.sync(`${path.join(datadir, folder)}/logo.{png,svg}`)
63
+ if (logofiles.length !== 1) {
64
+ results.push({
65
+ type: 'error',
66
+ message: `${folder} has ${logofiles.length} logo files, make sure your logo is either logo.png OR logo.svg`,
67
+ })
68
+ }
69
+
70
+ // Validate the data file
71
+ const v = new Validator()
72
+ const result = v.validate(data, TOKEN_DATA_SCHEMA as any)
73
+ if (!result.valid) {
74
+ for (const error of result.errors) {
75
+ results.push({
76
+ type: 'error',
77
+ message: `${folder}: ${error.property}: ${error.message}`,
78
+ })
79
+ }
80
+
81
+ // Since the data file is invalid, we can't continue validating the rest of the files
82
+ continue
83
+ }
84
+
85
+ // Validate each token configuration
86
+ for (const [chain, token] of Object.entries(data.tokens)) {
87
+ // Check to make sure the website will load
88
+ for (let i = 0; i < 5; i++) {
89
+ try {
90
+ await fetch(data.website)
91
+ break
92
+ } catch (err) {
93
+ if (i < 4) {
94
+ await sleep(5000)
95
+ } else {
96
+ results.push({
97
+ type: 'error',
98
+ message: `${folder} on chain ${chain} website did not load`,
99
+ })
100
+ }
101
+ }
102
+ }
103
+
104
+ // Validate any standard tokens
105
+ if (folder !== 'ETH' && data.nonstandard !== true) {
106
+ const contract = new ethers.Contract(
107
+ token.address,
108
+ TOKEN_ABI,
109
+ NETWORK_DATA[chain].provider
110
+ )
111
+
112
+ // Check that the token exists on this chain
113
+ if ((await contract.provider.getCode(token.address)) === '0x') {
114
+ results.push({
115
+ type: 'error',
116
+ message: `${folder} on chain ${chain} token ${token.address} does not exist`,
117
+ })
118
+ }
119
+
120
+ // Check that the token has the correct decimals
121
+ if (token.overrides?.decimals === undefined) {
122
+ try {
123
+ if (data.decimals !== (await contract.decimals())) {
124
+ results.push({
125
+ type: 'error',
126
+ message: `${folder} on chain ${chain} token ${token.address} has incorrect decimals`,
127
+ })
128
+ }
129
+ } catch (err) {
130
+ results.push({
131
+ type: 'error',
132
+ message: `${folder} on chain ${chain} token ${token.address} failed to get decimals`,
133
+ })
134
+ }
135
+ } else {
136
+ results.push({
137
+ type: 'warning',
138
+ message: `${folder} on chain ${chain} token ${token.address} has overridden decimals`,
139
+ })
140
+ }
141
+
142
+ // Check that the token has the correct symbol
143
+ if (token.overrides?.symbol === undefined) {
144
+ try {
145
+ if (data.symbol !== (await contract.symbol())) {
146
+ results.push({
147
+ type: 'error',
148
+ message: `${folder} on chain ${chain} token ${token.address} has incorrect symbol`,
149
+ })
150
+ }
151
+ } catch (err) {
152
+ results.push({
153
+ type: 'error',
154
+ message: `${folder} on chain ${chain} token ${token.address} failed to get symbol`,
155
+ })
156
+ }
157
+ } else {
158
+ results.push({
159
+ type: 'warning',
160
+ message: `${folder} on chain ${chain} token ${token.address} has overridden symbol`,
161
+ })
162
+ }
163
+
164
+ // Check that the token has the correct name
165
+ if (token.overrides?.name === undefined) {
166
+ try {
167
+ if (data.name !== (await contract.name())) {
168
+ results.push({
169
+ type: 'error',
170
+ message: `${folder} on chain ${chain} token ${token.address} has incorrect name`,
171
+ })
172
+ }
173
+ } catch (err) {
174
+ results.push({
175
+ type: 'error',
176
+ message: `${folder} on chain ${chain} token ${token.address} failed to get name`,
177
+ })
178
+ }
179
+ } else {
180
+ results.push({
181
+ type: 'warning',
182
+ message: `${folder} on chain ${chain} token ${token.address} has overridden name`,
183
+ })
184
+ }
185
+
186
+ // Check that the Ethereum token exists in the CG token list
187
+ if (chain === 'ethereum') {
188
+ const found = cg.tokens.find((t) => {
189
+ return t.address.toLowerCase() === token.address.toLowerCase()
190
+ })
191
+
192
+ // Trigger manual review if the Ethereum token is not in the CG token list
193
+ if (!found) {
194
+ results.push({
195
+ type: 'warning',
196
+ message: `${folder} on chain ${chain} token ${token.address} not found on CoinGecko token list`,
197
+ })
198
+ }
199
+ }
200
+
201
+ if (chain.startsWith('optimism')) {
202
+ const factory = new ethers.Contract(
203
+ '0x4200000000000000000000000000000000000012',
204
+ FACTORY_ABI,
205
+ NETWORK_DATA[chain].provider
206
+ )
207
+
208
+ const events = await factory.queryFilter(
209
+ factory.filters.StandardL2TokenCreated(undefined, token.address)
210
+ )
211
+
212
+ // Trigger review if not created by standard token factory.
213
+ if (events.length === 0) {
214
+ results.push({
215
+ type: 'warning',
216
+ message: `${folder} on chain ${chain} token ${token.address} not created by standard token factory`,
217
+ })
218
+ }
219
+ } else {
220
+ // Make sure the token is verified on Etherscan.
221
+ // Etherscan API is heavily rate limited, so sleep for 1s to avoid errors.
222
+ await sleep(1000)
223
+ const { qResult } = await (
224
+ await fetch(
225
+ `https://api${
226
+ chain === 'ethereum' ? '' : `-${chain}`
227
+ }.etherscan.io/api?` +
228
+ new URLSearchParams({
229
+ module: 'contract',
230
+ action: 'getsourcecode',
231
+ address: token.address,
232
+ apikey: process.env.ETHERSCAN_API_KEY,
233
+ })
234
+ )
235
+ ).json()
236
+
237
+ // Trigger review if code not verified on Etherscan
238
+ if (qResult[0].ABI === 'Contract source code not verified') {
239
+ results.push({
240
+ type: 'warning',
241
+ message: `${folder} on chain ${chain} token ${token.address} code not verified on Etherscan`,
242
+ })
243
+ }
244
+ }
245
+ } else {
246
+ results.push({
247
+ type: 'warning',
248
+ message: `${folder} on chain ${chain} token ${token.address} is a nonstandard token`,
249
+ })
250
+ }
251
+ }
252
+ }
253
+
254
+ // Verify that the final generated token list is valid
255
+ const list = generate(datadir)
256
+ const ajv = new Ajv({ allErrors: true, verbose: true })
257
+ addFormats(ajv)
258
+ const validator = ajv.compile(schema)
259
+ if (!validator(list)) {
260
+ results.push({
261
+ type: 'error',
262
+ message: `final token list is invalid: ${JSON.stringify(
263
+ validator.errors,
264
+ null,
265
+ 2
266
+ )}`,
267
+ })
268
+ }
269
+
270
+ return results
271
+ }
@@ -0,0 +1,87 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`'generate' script parse data dir and compile correct token list 1`] = `
4
+ Object {
5
+ "keywords": Array [
6
+ "scaling",
7
+ "layer2",
8
+ "infrastructure",
9
+ ],
10
+ "logoURI": "https://ethereum-optimism.github.io/optimism.svg",
11
+ "name": "Optimism",
12
+ "timestamp": "2022-08-17T17:00:00.000Z",
13
+ "tokens": Array [
14
+ Object {
15
+ "address": "0x0000000000000000000000000000000000000000",
16
+ "chainId": 1,
17
+ "decimals": 18,
18
+ "extensions": Object {
19
+ "optimismBridgeAddress": "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1",
20
+ },
21
+ "logoURI": "https://ethereum-optimism.github.io/data/ETH/logo.svg",
22
+ "name": "Ether",
23
+ "symbol": "ETH",
24
+ },
25
+ Object {
26
+ "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
27
+ "chainId": 10,
28
+ "decimals": 18,
29
+ "extensions": Object {
30
+ "optimismBridgeAddress": "0x4200000000000000000000000000000000000010",
31
+ },
32
+ "logoURI": "https://ethereum-optimism.github.io/data/ETH/logo.svg",
33
+ "name": "Ether",
34
+ "symbol": "ETH",
35
+ },
36
+ Object {
37
+ "address": "0x0000000000000000000000000000000000000000",
38
+ "chainId": 42,
39
+ "decimals": 18,
40
+ "extensions": Object {
41
+ "optimismBridgeAddress": "0x22F24361D548e5FaAfb36d1437839f080363982B",
42
+ },
43
+ "logoURI": "https://ethereum-optimism.github.io/data/ETH/logo.svg",
44
+ "name": "Ether",
45
+ "symbol": "ETH",
46
+ },
47
+ Object {
48
+ "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000",
49
+ "chainId": 69,
50
+ "decimals": 18,
51
+ "extensions": Object {
52
+ "optimismBridgeAddress": "0x4200000000000000000000000000000000000010",
53
+ },
54
+ "logoURI": "https://ethereum-optimism.github.io/data/ETH/logo.svg",
55
+ "name": "Ether",
56
+ "symbol": "ETH",
57
+ },
58
+ Object {
59
+ "address": "0x4200000000000000000000000000000000000042",
60
+ "chainId": 10,
61
+ "decimals": 18,
62
+ "extensions": Object {
63
+ "optimismBridgeAddress": "0x4200000000000000000000000000000000000010",
64
+ },
65
+ "logoURI": "https://ethereum-optimism.github.io/data/OP/logo.png",
66
+ "name": "Optimism",
67
+ "symbol": "OP",
68
+ },
69
+ Object {
70
+ "address": "0x4200000000000000000000000000000000000042",
71
+ "chainId": 69,
72
+ "decimals": 18,
73
+ "extensions": Object {
74
+ "optimismBridgeAddress": "0x4200000000000000000000000000000000000010",
75
+ },
76
+ "logoURI": "https://ethereum-optimism.github.io/data/OP/logo.png",
77
+ "name": "Optimism",
78
+ "symbol": "OP",
79
+ },
80
+ ],
81
+ "version": Object {
82
+ "major": 4,
83
+ "minor": 3,
84
+ "patch": Any<Number>,
85
+ },
86
+ }
87
+ `;
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "Ether",
3
+ "symbol": "ETH",
4
+ "decimals": 18,
5
+ "description": "Ethereum",
6
+ "website": "https://ethereum.org",
7
+ "tokens": {
8
+ "ethereum": {
9
+ "address": "0x0000000000000000000000000000000000000000"
10
+ },
11
+ "optimism": {
12
+ "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
13
+ },
14
+ "kovan": {
15
+ "address": "0x0000000000000000000000000000000000000000"
16
+ },
17
+ "optimism-kovan": {
18
+ "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,13 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 32 32">
2
+ <g fill="none" fill-rule="evenodd">
3
+ <circle cx="16" cy="16" r="16" fill="#627EEA"/>
4
+ <g fill="#FFF" fill-rule="nonzero">
5
+ <path fill-opacity=".602" d="M16.498 4v8.87l7.497 3.35z"/>
6
+ <path d="M16.498 4L9 16.22l7.498-3.35z"/>
7
+ <path fill-opacity=".602" d="M16.498 21.968v6.027L24 17.616z"/>
8
+ <path d="M16.498 27.995v-6.028L9 17.616z"/>
9
+ <path fill-opacity=".2" d="M16.498 20.573l7.497-4.353-7.497-3.348z"/>
10
+ <path fill-opacity=".602" d="M9 16.22l7.498 4.353v-7.701z"/>
11
+ </g>
12
+ </g>
13
+ </svg>
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "Optimism",
3
+ "symbol": "OP",
4
+ "decimals": 18,
5
+ "nobridge": true,
6
+ "description": "Optimism",
7
+ "website": "https://optimism.io",
8
+ "tokens": {
9
+ "optimism": {
10
+ "address": "0x4200000000000000000000000000000000000042"
11
+ },
12
+ "optimism-kovan": {
13
+ "address": "0x4200000000000000000000000000000000000042"
14
+ }
15
+ }
16
+ }
Binary file
@@ -0,0 +1,12 @@
1
+ import { filterTokens, defaultTokenList, defaultSymbols } from '../src'
2
+
3
+ describe('default tokens', () => {
4
+ it('all default tokens symbols should exist on the default token list without a typo', () => {
5
+ expect(defaultSymbols.size).toBeGreaterThan(0)
6
+ defaultSymbols.forEach((symbol) => {
7
+ expect(
8
+ filterTokens(defaultTokenList.tokens, { filterTerm: symbol }).length
9
+ ).toBeGreaterThan(0)
10
+ })
11
+ })
12
+ })
@@ -0,0 +1,20 @@
1
+ import path from 'path'
2
+
3
+ import { generate } from '../src/generate'
4
+
5
+ test("'generate' script parse data dir and compile correct token list", async () => {
6
+ jest
7
+ .spyOn(path, 'resolve')
8
+ .mockReturnValueOnce(path.resolve(__dirname, 'data'))
9
+
10
+ const mockDate = new Date(1660755600000)
11
+ jest.spyOn(global, 'Date').mockImplementation(() => mockDate as any)
12
+
13
+ const tokenList = generate(path.resolve(__dirname, 'data'))
14
+ expect(tokenList).toMatchSnapshot({
15
+ timestamp: mockDate.toISOString(),
16
+ version: {
17
+ patch: expect.any(Number),
18
+ }
19
+ })
20
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "outDir": "./dist",
4
+ "module": "commonjs",
5
+ "target": "es2017",
6
+ "sourceMap": true,
7
+ "esModuleInterop": true,
8
+ "composite": true,
9
+ "resolveJsonModule": true,
10
+ "declaration": true,
11
+ "noImplicitAny": false,
12
+ "removeComments": true,
13
+ "noLib": false,
14
+ "emitDecoratorMetadata": true,
15
+ "experimentalDecorators": true,
16
+ "typeRoots": ["node_modules/@types"]
17
+ },
18
+ "exclude": ["node_modules/**/*", "dist"],
19
+ "include": [
20
+ "index.ts",
21
+ "optimism.tokenlist.json",
22
+ "optimism.defaulttokens.json",
23
+ "package.json",
24
+ "src/**/*",
25
+ "bin/**/*"
26
+ ]
27
+ }