@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/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@eth-optimism/tokenlist",
3
+ "description": "[Optimism] token list",
4
+ "version": "4.3.1",
5
+ "main": "dist/eth-optimism-tokenlist.cjs.js",
6
+ "module": "dist/eth-optimism-tokenlist.esm.js",
7
+ "homepage": "https://github.com/ethereum-optimism/tokenlist#readme",
8
+ "license": "MIT",
9
+ "author": "roninjin10",
10
+ "preconstruct": {
11
+ "entrypoints": [
12
+ "index.ts"
13
+ ]
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/ethereum-optimism/tokenlist.git"
18
+ },
19
+ "scripts": {
20
+ "validate": "ts-node ./bin/cli.ts validate",
21
+ "generate": "ts-node ./bin/cli.ts generate",
22
+ "generate:ci": "yarn generate --datadir ./data --outfile optimism.tokenlist.json",
23
+ "build": "preconstruct build",
24
+ "build:ci": "yarn generate:ci && yarn build",
25
+ "release": "yarn build:ci && npm publish",
26
+ "test": "jest --detectOpenHandles",
27
+ "lint:check": "eslint . --max-warnings=0",
28
+ "lint:fix": "eslint --fix .",
29
+ "lint": "yarn lint:fix && yarn lint:check",
30
+ "postinstall": "preconstruct dev",
31
+ "start-bot": "ts-node ./src/bot.ts"
32
+ },
33
+ "devDependencies": {
34
+ "@actions/core": "^1.4.0",
35
+ "@babel/eslint-parser": "^7.18.2",
36
+ "@eth-optimism/common-ts": "^0.6.5",
37
+ "@eth-optimism/contracts": "^0.5.7",
38
+ "@eth-optimism/core-utils": "^0.9.3",
39
+ "@preconstruct/cli": "^2.2.2",
40
+ "@types/glob": "^8.0.0",
41
+ "@types/jest": "^29.0.3",
42
+ "@types/node": "^12.0.0",
43
+ "@types/uuid": "^8.3.4",
44
+ "@typescript-eslint/eslint-plugin": "^5.26.0",
45
+ "@typescript-eslint/parser": "^4.26.0",
46
+ "@uniswap/token-lists": "^1.0.0-beta.30",
47
+ "ajv": "^8.11.2",
48
+ "ajv-formats": "^2.1.1",
49
+ "commander": "^9.4.0",
50
+ "eslint": "^8.16.0",
51
+ "eslint-config-prettier": "^8.3.0",
52
+ "eslint-config-standard": "^16.0.3",
53
+ "eslint-plugin-import": "^2.26.0",
54
+ "eslint-plugin-jsdoc": "^35.1.2",
55
+ "eslint-plugin-node": "^11.1.0",
56
+ "eslint-plugin-prefer-arrow": "^1.2.3",
57
+ "eslint-plugin-prettier": "^4.0.0",
58
+ "eslint-plugin-promise": "^5.1.0",
59
+ "eslint-plugin-react": "^7.24.0",
60
+ "eslint-plugin-unicorn": "^42.0.0",
61
+ "ethers": "^5.4.1",
62
+ "extract-zip": "^2.0.1",
63
+ "glob": "^8.0.3",
64
+ "jest": "^28.1.3",
65
+ "jsonschema": "^1.4.1",
66
+ "mocha": "^8.4.0",
67
+ "node-fetch": "^2.6.7",
68
+ "octokit": "^2.0.7",
69
+ "prettier": "^2.3.1",
70
+ "ts-jest": "^29.0.1",
71
+ "ts-mocha": "^10.0.0",
72
+ "ts-node": "^10.8.2",
73
+ "typescript": "^4.6.2",
74
+ "uuid": "^9.0.0"
75
+ },
76
+ "dependencies": {
77
+ "@babel/preset-typescript": "^7.18.6",
78
+ "babel": "^6.23.0"
79
+ }
80
+ }
package/src/abi.ts ADDED
@@ -0,0 +1,77 @@
1
+ export const FACTORY_ABI = [
2
+ {
3
+ anonymous: false,
4
+ inputs: [
5
+ {
6
+ indexed: true,
7
+ internalType: 'address',
8
+ name: '_l1Token',
9
+ type: 'address',
10
+ },
11
+ {
12
+ indexed: true,
13
+ internalType: 'address',
14
+ name: '_l2Token',
15
+ type: 'address',
16
+ },
17
+ ],
18
+ name: 'StandardL2TokenCreated',
19
+ type: 'event',
20
+ },
21
+ {
22
+ inputs: [
23
+ { internalType: 'address', name: '_l1Token', type: 'address' },
24
+ { internalType: 'string', name: '_name', type: 'string' },
25
+ { internalType: 'string', name: '_symbol', type: 'string' },
26
+ ],
27
+ name: 'createStandardL2Token',
28
+ outputs: [],
29
+ stateMutability: 'nonpayable',
30
+ type: 'function',
31
+ },
32
+ ]
33
+
34
+ export const TOKEN_ABI = [
35
+ {
36
+ constant: true,
37
+ inputs: [],
38
+ name: 'name',
39
+ outputs: [
40
+ {
41
+ name: '',
42
+ type: 'string',
43
+ },
44
+ ],
45
+ payable: false,
46
+ stateMutability: 'view',
47
+ type: 'function',
48
+ },
49
+ {
50
+ constant: true,
51
+ inputs: [],
52
+ name: 'decimals',
53
+ outputs: [
54
+ {
55
+ name: '',
56
+ type: 'uint8',
57
+ },
58
+ ],
59
+ payable: false,
60
+ stateMutability: 'view',
61
+ type: 'function',
62
+ },
63
+ {
64
+ constant: true,
65
+ inputs: [],
66
+ name: 'symbol',
67
+ outputs: [
68
+ {
69
+ name: '',
70
+ type: 'string',
71
+ },
72
+ ],
73
+ payable: false,
74
+ stateMutability: 'view',
75
+ type: 'function',
76
+ },
77
+ ]
package/src/bot.ts ADDED
@@ -0,0 +1,236 @@
1
+ import crypto from 'crypto'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+
5
+ import {
6
+ BaseServiceV2,
7
+ ExpressRouter,
8
+ validators,
9
+ } from '@eth-optimism/common-ts'
10
+ import { Octokit } from 'octokit'
11
+ import extract from 'extract-zip'
12
+ import uuid from 'uuid'
13
+
14
+ import { version } from '../package.json'
15
+
16
+ type TOptions = {
17
+ secret: string
18
+ pat: string
19
+ tempdir: string
20
+ }
21
+
22
+ type TMetrics = {}
23
+
24
+ type TState = {
25
+ gh: Octokit
26
+ }
27
+
28
+ export class Bot extends BaseServiceV2<TOptions, TMetrics, TState> {
29
+ constructor(options?: Partial<TOptions>) {
30
+ super({
31
+ name: 'token-list-bot',
32
+ version,
33
+ options,
34
+ optionsSpec: {
35
+ secret: {
36
+ secret: true,
37
+ desc: 'secret used to check webhook validity',
38
+ validator: validators.str,
39
+ },
40
+ pat: {
41
+ secret: true,
42
+ desc: 'personal access token for github',
43
+ validator: validators.str,
44
+ },
45
+ tempdir: {
46
+ desc: 'temporary directory for storing files',
47
+ validator: validators.str,
48
+ default: './tmp',
49
+ },
50
+ },
51
+ metricsSpec: {
52
+ // ...
53
+ },
54
+ })
55
+ }
56
+
57
+ async init(): Promise<void> {
58
+ this.state.gh = new Octokit({
59
+ auth: this.options.pat,
60
+ })
61
+
62
+ // Create the temporary directory if it doesn't exist.
63
+ if (!fs.existsSync(this.options.tempdir)) {
64
+ fs.mkdirSync(this.options.tempdir)
65
+ }
66
+ }
67
+
68
+ async routes(router: ExpressRouter): Promise<void> {
69
+ router.post('/webhook', async (req: any, res: any) => {
70
+ const id = uuid.v4()
71
+ try {
72
+ // We'll need this later
73
+ const owner = 'ethereum-optimism'
74
+ const repo = 'ethereum-optimism.github.io'
75
+
76
+ // Compute the HMAC of the request body
77
+ const sig = Buffer.from(req.get('X-Hub-Signature-256') || '', 'utf8')
78
+ const hmac = crypto.createHmac('sha256', this.options.secret)
79
+ const digest = Buffer.from(
80
+ `sha256=${hmac.update(req.rawBody).digest('hex')}`,
81
+ 'utf8'
82
+ )
83
+
84
+ // Check that the HMAC is valid
85
+ if (!crypto.timingSafeEqual(digest, sig)) {
86
+ return res
87
+ .status(200)
88
+ .json({ ok: false, message: 'invalid signature on workflow' })
89
+ }
90
+
91
+ // Make sure we're only looking at "Validate PR" workflows
92
+ if (req.body.workflow.name !== 'Validate PR') {
93
+ return res
94
+ .status(200)
95
+ .json({ ok: false, message: 'incorrect workflow' })
96
+ }
97
+
98
+ const artifactQueryResponse = await this.state.gh.request(
99
+ 'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts',
100
+ {
101
+ owner,
102
+ repo,
103
+ run_id: req.body.workflow_run.id,
104
+ }
105
+ )
106
+
107
+ if (artifactQueryResponse.data.total_count !== 1) {
108
+ return res
109
+ .status(200)
110
+ .json({ ok: false, message: 'incorrect number of artifacts' })
111
+ }
112
+
113
+ const artifact = artifactQueryResponse.data.artifacts[0]
114
+ if (artifact.name !== 'logs-artifact') {
115
+ return res
116
+ .status(200)
117
+ .json({ ok: false, message: 'incorrect artifact name' })
118
+ }
119
+
120
+ const artifactDownloadResponse = await this.state.gh.request(
121
+ 'GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}',
122
+ {
123
+ owner,
124
+ repo,
125
+ artifact_id: artifact.id,
126
+ archive_format: 'zip',
127
+ }
128
+ )
129
+
130
+ const tempdir = path.resolve(this.options.tempdir, id)
131
+ if (!fs.existsSync(tempdir)) {
132
+ fs.mkdirSync(tempdir)
133
+ }
134
+
135
+ const temploc = path.resolve(tempdir, `${artifact.id}.zip`)
136
+ const tempout = path.resolve(tempdir, `${artifact.id}-out`)
137
+
138
+ fs.writeFileSync(
139
+ temploc,
140
+ Buffer.from(
141
+ new Uint8Array(artifactDownloadResponse.data as ArrayBuffer)
142
+ )
143
+ )
144
+
145
+ await extract(temploc, {
146
+ dir: path.resolve(tempout),
147
+ })
148
+
149
+ const pr = fs.readFileSync(path.resolve(tempout, 'pr.txt'), 'utf8')
150
+ const err = fs.readFileSync(path.resolve(tempout, 'err.txt'), 'utf8')
151
+ const std = fs.readFileSync(path.resolve(tempout, 'std.txt'), 'utf8')
152
+
153
+ if (err.length > 0) {
154
+ await this.state.gh.request(
155
+ 'POST /repos/{owner}/{repo}/issues/{issue_number}/comments',
156
+ {
157
+ owner,
158
+ repo,
159
+ issue_number: parseInt(pr, 10),
160
+ body: `Got some errors while validating this PR. You will need to fix these errors before this PR can be reviewed.\n\`\`\`\n${err}\`\`\``,
161
+ }
162
+ )
163
+
164
+ return res
165
+ .status(200)
166
+ .json({ ok: true, message: 'ok with error comment on PR' })
167
+ }
168
+
169
+ const warns = std.split('\n').filter((line) => {
170
+ return line.startsWith('warning')
171
+ })
172
+
173
+ if (warns.length > 0) {
174
+ await this.state.gh.request(
175
+ 'POST /repos/{owner}/{repo}/issues/{issue_number}/comments',
176
+ {
177
+ owner,
178
+ repo,
179
+ issue_number: parseInt(pr, 10),
180
+ body: `Got some warnings while validating this PR. This is usually OK but this PR will require manual review if you are unable to resolve these warnings.\n\`\`\`\n${warns.join(
181
+ '\n'
182
+ )}\n\`\`\``,
183
+ }
184
+ )
185
+
186
+ await this.state.gh.request(
187
+ 'POST /repos/{owner}/{repo}/issues/{issue_number}/labels',
188
+ {
189
+ owner,
190
+ repo,
191
+ issue_number: parseInt(pr, 10),
192
+ labels: ['requires-manual-review'],
193
+ }
194
+ )
195
+
196
+ return res
197
+ .status(200)
198
+ .json({ ok: true, message: 'ok with warning comment on PR' })
199
+ }
200
+
201
+ // Can safely remove requires-manual-review if we got here without warnings! Sometimes the
202
+ // label will be on the PR because a previous iteration of the workflow had warnings but the
203
+ // warnings were cleared up.
204
+ await this.state.gh.request(
205
+ 'DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}',
206
+ {
207
+ owner,
208
+ repo,
209
+ issue_number: parseInt(pr, 10),
210
+ name: 'requires-manual-review',
211
+ }
212
+ )
213
+
214
+ return res.status(200).json({ ok: true, message: 'noice!' })
215
+ } catch (err) {
216
+ this.logger.error(err)
217
+ return res.status(500).json({ ok: false, message: 'unexpected error' })
218
+ } finally {
219
+ // Always clean up the tempdir.
220
+ const tempdir = path.resolve(this.options.tempdir, id)
221
+ if (fs.existsSync(tempdir)) {
222
+ fs.rmdirSync(tempdir, { recursive: true })
223
+ }
224
+ }
225
+ })
226
+ }
227
+
228
+ async main(): Promise<void> {
229
+ // nothing to do here
230
+ }
231
+ }
232
+
233
+ if (require.main === module) {
234
+ const bot = new Bot()
235
+ bot.run()
236
+ }
package/src/chains.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { ethers } from 'ethers'
2
+
3
+ export const NETWORK_DATA = {
4
+ ethereum: {
5
+ id: 1,
6
+ name: 'Mainnet',
7
+ provider: new ethers.providers.InfuraProvider('homestead'),
8
+ layer: 1,
9
+ pair: 'optimism',
10
+ bridge: '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1',
11
+ },
12
+ optimism: {
13
+ id: 10,
14
+ name: 'Optimism',
15
+ provider: new ethers.providers.StaticJsonRpcProvider(
16
+ 'https://mainnet.optimism.io'
17
+ ),
18
+ layer: 2,
19
+ pair: 'ethereum',
20
+ bridge: '0x4200000000000000000000000000000000000010',
21
+ },
22
+ kovan: {
23
+ id: 42,
24
+ name: 'Kovan',
25
+ provider: new ethers.providers.InfuraProvider('kovan'),
26
+ layer: 1,
27
+ pair: 'optimism-kovan',
28
+ bridge: '0x22F24361D548e5FaAfb36d1437839f080363982B',
29
+ },
30
+ 'optimism-kovan': {
31
+ id: 69,
32
+ name: 'Optimism Kovan',
33
+ provider: new ethers.providers.StaticJsonRpcProvider(
34
+ 'https://kovan.optimism.io'
35
+ ),
36
+ layer: 2,
37
+ pair: 'kovan',
38
+ bridge: '0x4200000000000000000000000000000000000010',
39
+ },
40
+ goerli: {
41
+ id: 5,
42
+ name: 'Goerli',
43
+ provider: new ethers.providers.InfuraProvider('goerli'),
44
+ layer: 1,
45
+ pair: 'optimism-goerli',
46
+ bridge: '0x636Af16bf2f682dD3109e60102b8E1A089FedAa8',
47
+ },
48
+ 'optimism-goerli': {
49
+ id: 420,
50
+ name: 'Optimism Goerli',
51
+ provider: new ethers.providers.StaticJsonRpcProvider(
52
+ 'https://goerli.optimism.io'
53
+ ),
54
+ layer: 2,
55
+ pair: 'goerli',
56
+ bridge: '0x4200000000000000000000000000000000000010',
57
+ },
58
+ }
@@ -0,0 +1,69 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+
4
+ import { glob } from 'glob'
5
+
6
+ import { version } from '../package.json'
7
+ import { NETWORK_DATA } from './chains'
8
+ import { TokenData } from './types'
9
+
10
+ /**
11
+ * Base URL where static assets are hosted.
12
+ */
13
+ const BASE_URL = 'https://ethereum-optimism.github.io'
14
+
15
+ /**
16
+ * Generates a token list from the data in the data folder.
17
+ *
18
+ * @param datadir Directory containing data files.
19
+ *
20
+ * @returns Generated token list JSON object.
21
+ */
22
+ export const generate = (datadir: string) => {
23
+ return fs
24
+ .readdirSync(datadir)
25
+ .sort((a, b) => {
26
+ return a.toLowerCase().localeCompare(b.toLowerCase())
27
+ })
28
+ .map((folder) => {
29
+ const data: TokenData = JSON.parse(
30
+ fs.readFileSync(path.join(datadir, folder, 'data.json'), 'utf8')
31
+ )
32
+ const logofiles = glob.sync(
33
+ `${path.join(datadir, folder)}/logo.{png,svg}`
34
+ )
35
+ const logoext = logofiles[0].endsWith('png') ? 'png' : 'svg'
36
+ return Object.entries(data.tokens).map(([chain, token]) => {
37
+ return {
38
+ chainId: NETWORK_DATA[chain].id,
39
+ address: token.address,
40
+ name: token.overrides?.name ?? data.name,
41
+ symbol: token.overrides?.symbol ?? data.symbol,
42
+ decimals: token.overrides?.decimals ?? data.decimals,
43
+ logoURI: `${BASE_URL}/data/${folder}/logo.${logoext}`,
44
+ extensions: {
45
+ optimismBridgeAddress:
46
+ token.overrides?.bridge ?? NETWORK_DATA[chain].bridge,
47
+ },
48
+ }
49
+ })
50
+ })
51
+ .reduce(
52
+ (list, tokens) => {
53
+ list.tokens = list.tokens.concat(tokens)
54
+ return list
55
+ },
56
+ {
57
+ name: 'Optimism',
58
+ logoURI: `${BASE_URL}/optimism.svg`,
59
+ keywords: ['scaling', 'layer2', 'infrastructure'],
60
+ timestamp: new Date().toISOString(),
61
+ tokens: [],
62
+ version: {
63
+ major: parseInt(version.split('.')[0], 10),
64
+ minor: parseInt(version.split('.')[1], 10),
65
+ patch: parseInt(version.split('.')[2], 10),
66
+ },
67
+ }
68
+ )
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,71 @@
1
+ import _extendedList from '../optimism.tokenlist.json'
2
+ import _defaultSymbols from '../optimism.defaulttokens.json'
3
+
4
+ export interface TokenList {
5
+ name: string
6
+ logoURI: string
7
+ tokens: TokenListItem[]
8
+ }
9
+
10
+ export interface TokenListItem {
11
+ chainId: number
12
+ address: string
13
+ name: string
14
+ symbol: string
15
+ decimals: number
16
+ logoURI?: string
17
+ extensions?: {
18
+ optimismBridgeAddress?: string
19
+ ens?: string
20
+ }
21
+ }
22
+
23
+ export enum TokenListChoice {
24
+ 'EXTENDED' = 'EXTENDED',
25
+ 'DEFAULT' = 'DEFAULT',
26
+ 'DEFAULT_DEVNET' = 'DEFAULT_DEVNET',
27
+ 'EXTENDED_DEVNET' = 'EXTENDED_DEVNET',
28
+ }
29
+
30
+ export const extendedList = _extendedList
31
+
32
+ export interface FilterOptions {
33
+ chainId?: number
34
+ filterTerm?: string
35
+ }
36
+
37
+ export const filterTokens = (
38
+ list: TokenList['tokens'],
39
+ { chainId, filterTerm }: FilterOptions = {}
40
+ ) => {
41
+ return (
42
+ list
43
+ // filter by chainId
44
+ .filter((token) => !chainId || token.chainId === chainId)
45
+ // filter by filterTerm
46
+ .filter(
47
+ (token) =>
48
+ !filterTerm ||
49
+ token.address.toLowerCase().includes(filterTerm.toLowerCase()) ||
50
+ token.symbol.toLowerCase().includes(filterTerm.toLowerCase()) ||
51
+ token.name.toLowerCase().includes(filterTerm.toLowerCase())
52
+ )
53
+ )
54
+ }
55
+
56
+ export const defaultSymbols = new Set(_defaultSymbols)
57
+
58
+ export const defaultTokenList = {
59
+ ...extendedList,
60
+ name: 'Default Token List',
61
+ tokens: extendedList.tokens.filter(({ symbol }) =>
62
+ defaultSymbols.has(symbol)
63
+ ),
64
+ }
65
+
66
+ export const tokensByNetwork = {
67
+ 1: filterTokens(extendedList.tokens, { chainId: 1 }),
68
+ 5: filterTokens(extendedList.tokens, { chainId: 5 }),
69
+ 10: filterTokens(extendedList.tokens, { chainId: 10 }),
70
+ 420: filterTokens(extendedList.tokens, { chainId: 420 }),
71
+ }