1 | var $schema = "http://json-schema.org/draft-07/schema#";
|
2 | var $id = "https://uniswap.org/tokenlist.schema.json";
|
3 | var title = "Uniswap Token List";
|
4 | var description = "Schema for lists of tokens compatible with the Uniswap Interface";
|
5 | var definitions = {
|
6 | Version: {
|
7 | type: "object",
|
8 | description: "The version of the list, used in change detection",
|
9 | examples: [
|
10 | {
|
11 | major: 1,
|
12 | minor: 0,
|
13 | patch: 0
|
14 | }
|
15 | ],
|
16 | additionalProperties: false,
|
17 | properties: {
|
18 | major: {
|
19 | type: "integer",
|
20 | description: "The major version of the list. Must be incremented when tokens are removed from the list or token addresses are changed.",
|
21 | minimum: 0,
|
22 | examples: [
|
23 | 1,
|
24 | 2
|
25 | ]
|
26 | },
|
27 | minor: {
|
28 | type: "integer",
|
29 | description: "The minor version of the list. Must be incremented when tokens are added to the list.",
|
30 | minimum: 0,
|
31 | examples: [
|
32 | 0,
|
33 | 1
|
34 | ]
|
35 | },
|
36 | patch: {
|
37 | type: "integer",
|
38 | description: "The patch version of the list. Must be incremented for any changes to the list.",
|
39 | minimum: 0,
|
40 | examples: [
|
41 | 0,
|
42 | 1
|
43 | ]
|
44 | }
|
45 | },
|
46 | required: [
|
47 | "major",
|
48 | "minor",
|
49 | "patch"
|
50 | ]
|
51 | },
|
52 | TagIdentifier: {
|
53 | type: "string",
|
54 | description: "The unique identifier of a tag",
|
55 | minLength: 1,
|
56 | maxLength: 10,
|
57 | pattern: "^[\\w]+$",
|
58 | examples: [
|
59 | "compound",
|
60 | "stablecoin"
|
61 | ]
|
62 | },
|
63 | ExtensionIdentifier: {
|
64 | type: "string",
|
65 | description: "The name of a token extension property",
|
66 | minLength: 1,
|
67 | maxLength: 40,
|
68 | pattern: "^[\\w]+$",
|
69 | examples: [
|
70 | "color",
|
71 | "is_fee_on_transfer",
|
72 | "aliases"
|
73 | ]
|
74 | },
|
75 | ExtensionMap: {
|
76 | type: "object",
|
77 | description: "An object containing any arbitrary or vendor-specific token metadata",
|
78 | maxProperties: 10,
|
79 | propertyNames: {
|
80 | $ref: "#/definitions/ExtensionIdentifier"
|
81 | },
|
82 | additionalProperties: {
|
83 | $ref: "#/definitions/ExtensionValue"
|
84 | },
|
85 | examples: [
|
86 | {
|
87 | color: "#000000",
|
88 | is_verified_by_me: true
|
89 | },
|
90 | {
|
91 | "x-bridged-addresses-by-chain": {
|
92 | "1": {
|
93 | bridgeAddress: "0x4200000000000000000000000000000000000010",
|
94 | tokenAddress: "0x4200000000000000000000000000000000000010"
|
95 | }
|
96 | }
|
97 | }
|
98 | ]
|
99 | },
|
100 | ExtensionPrimitiveValue: {
|
101 | anyOf: [
|
102 | {
|
103 | type: "string",
|
104 | minLength: 1,
|
105 | maxLength: 42,
|
106 | examples: [
|
107 | "#00000"
|
108 | ]
|
109 | },
|
110 | {
|
111 | type: "boolean",
|
112 | examples: [
|
113 | true
|
114 | ]
|
115 | },
|
116 | {
|
117 | type: "number",
|
118 | examples: [
|
119 | 15
|
120 | ]
|
121 | },
|
122 | {
|
123 | type: "null"
|
124 | }
|
125 | ]
|
126 | },
|
127 | ExtensionValue: {
|
128 | anyOf: [
|
129 | {
|
130 | $ref: "#/definitions/ExtensionPrimitiveValue"
|
131 | },
|
132 | {
|
133 | type: "object",
|
134 | maxProperties: 10,
|
135 | propertyNames: {
|
136 | $ref: "#/definitions/ExtensionIdentifier"
|
137 | },
|
138 | additionalProperties: {
|
139 | $ref: "#/definitions/ExtensionValueInner0"
|
140 | }
|
141 | }
|
142 | ]
|
143 | },
|
144 | ExtensionValueInner0: {
|
145 | anyOf: [
|
146 | {
|
147 | $ref: "#/definitions/ExtensionPrimitiveValue"
|
148 | },
|
149 | {
|
150 | type: "object",
|
151 | maxProperties: 10,
|
152 | propertyNames: {
|
153 | $ref: "#/definitions/ExtensionIdentifier"
|
154 | },
|
155 | additionalProperties: {
|
156 | $ref: "#/definitions/ExtensionValueInner1"
|
157 | }
|
158 | }
|
159 | ]
|
160 | },
|
161 | ExtensionValueInner1: {
|
162 | anyOf: [
|
163 | {
|
164 | $ref: "#/definitions/ExtensionPrimitiveValue"
|
165 | }
|
166 | ]
|
167 | },
|
168 | TagDefinition: {
|
169 | type: "object",
|
170 | description: "Definition of a tag that can be associated with a token via its identifier",
|
171 | additionalProperties: false,
|
172 | properties: {
|
173 | name: {
|
174 | type: "string",
|
175 | description: "The name of the tag",
|
176 | pattern: "^[ \\w]+$",
|
177 | minLength: 1,
|
178 | maxLength: 20
|
179 | },
|
180 | description: {
|
181 | type: "string",
|
182 | description: "A user-friendly description of the tag",
|
183 | pattern: "^[ \\w\\.,:]+$",
|
184 | minLength: 1,
|
185 | maxLength: 200
|
186 | }
|
187 | },
|
188 | required: [
|
189 | "name",
|
190 | "description"
|
191 | ],
|
192 | examples: [
|
193 | {
|
194 | name: "Stablecoin",
|
195 | description: "A token with value pegged to another asset"
|
196 | }
|
197 | ]
|
198 | },
|
199 | TokenInfo: {
|
200 | type: "object",
|
201 | description: "Metadata for a single token in a token list",
|
202 | additionalProperties: false,
|
203 | properties: {
|
204 | chainId: {
|
205 | type: "integer",
|
206 | description: "The chain ID of the Ethereum network where this token is deployed",
|
207 | minimum: 1,
|
208 | examples: [
|
209 | 1,
|
210 | 42
|
211 | ]
|
212 | },
|
213 | address: {
|
214 | type: "string",
|
215 | description: "The checksummed address of the token on the specified chain ID",
|
216 | pattern: "^0x[a-fA-F0-9]{40}$",
|
217 | examples: [
|
218 | "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
219 | ]
|
220 | },
|
221 | decimals: {
|
222 | type: "integer",
|
223 | description: "The number of decimals for the token balance",
|
224 | minimum: 0,
|
225 | maximum: 255,
|
226 | examples: [
|
227 | 18
|
228 | ]
|
229 | },
|
230 | name: {
|
231 | type: "string",
|
232 | description: "The name of the token",
|
233 | minLength: 1,
|
234 | maxLength: 40,
|
235 | pattern: "^[ \\w.'+\\-%/À-ÖØ-öø-ÿ:&\\[\\]\\(\\)]+$",
|
236 | examples: [
|
237 | "USD Coin"
|
238 | ]
|
239 | },
|
240 | symbol: {
|
241 | type: "string",
|
242 | description: "The symbol for the token; must be alphanumeric",
|
243 | pattern: "^[a-zA-Z0-9+\\-%/$.]+$",
|
244 | minLength: 1,
|
245 | maxLength: 20,
|
246 | examples: [
|
247 | "USDC"
|
248 | ]
|
249 | },
|
250 | logoURI: {
|
251 | type: "string",
|
252 | description: "A URI to the token logo asset; if not set, interface will attempt to find a logo based on the token address; suggest SVG or PNG of size 64x64",
|
253 | format: "uri",
|
254 | examples: [
|
255 | "ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM"
|
256 | ]
|
257 | },
|
258 | tags: {
|
259 | type: "array",
|
260 | description: "An array of tag identifiers associated with the token; tags are defined at the list level",
|
261 | items: {
|
262 | $ref: "#/definitions/TagIdentifier"
|
263 | },
|
264 | maxItems: 10,
|
265 | examples: [
|
266 | "stablecoin",
|
267 | "compound"
|
268 | ]
|
269 | },
|
270 | extensions: {
|
271 | $ref: "#/definitions/ExtensionMap"
|
272 | }
|
273 | },
|
274 | required: [
|
275 | "chainId",
|
276 | "address",
|
277 | "decimals",
|
278 | "name",
|
279 | "symbol"
|
280 | ]
|
281 | }
|
282 | };
|
283 | var type = "object";
|
284 | var additionalProperties = false;
|
285 | var properties = {
|
286 | name: {
|
287 | type: "string",
|
288 | description: "The name of the token list",
|
289 | minLength: 1,
|
290 | maxLength: 30,
|
291 | pattern: "^[\\w ]+$",
|
292 | examples: [
|
293 | "My Token List"
|
294 | ]
|
295 | },
|
296 | timestamp: {
|
297 | type: "string",
|
298 | format: "date-time",
|
299 | description: "The timestamp of this list version; i.e. when this immutable version of the list was created"
|
300 | },
|
301 | version: {
|
302 | $ref: "#/definitions/Version"
|
303 | },
|
304 | tokens: {
|
305 | type: "array",
|
306 | description: "The list of tokens included in the list",
|
307 | items: {
|
308 | $ref: "#/definitions/TokenInfo"
|
309 | },
|
310 | minItems: 1,
|
311 | maxItems: 10000
|
312 | },
|
313 | keywords: {
|
314 | type: "array",
|
315 | description: "Keywords associated with the contents of the list; may be used in list discoverability",
|
316 | items: {
|
317 | type: "string",
|
318 | description: "A keyword to describe the contents of the list",
|
319 | minLength: 1,
|
320 | maxLength: 20,
|
321 | pattern: "^[\\w ]+$",
|
322 | examples: [
|
323 | "compound",
|
324 | "lending",
|
325 | "personal tokens"
|
326 | ]
|
327 | },
|
328 | maxItems: 20,
|
329 | uniqueItems: true
|
330 | },
|
331 | tags: {
|
332 | type: "object",
|
333 | description: "A mapping of tag identifiers to their name and description",
|
334 | propertyNames: {
|
335 | $ref: "#/definitions/TagIdentifier"
|
336 | },
|
337 | additionalProperties: {
|
338 | $ref: "#/definitions/TagDefinition"
|
339 | },
|
340 | maxProperties: 20,
|
341 | examples: [
|
342 | {
|
343 | stablecoin: {
|
344 | name: "Stablecoin",
|
345 | description: "A token with value pegged to another asset"
|
346 | }
|
347 | }
|
348 | ]
|
349 | },
|
350 | logoURI: {
|
351 | type: "string",
|
352 | description: "A URI for the logo of the token list; prefer SVG or PNG of size 256x256",
|
353 | format: "uri",
|
354 | examples: [
|
355 | "ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM"
|
356 | ]
|
357 | }
|
358 | };
|
359 | var required = [
|
360 | "name",
|
361 | "timestamp",
|
362 | "version",
|
363 | "tokens"
|
364 | ];
|
365 | var tokenlist_schema = {
|
366 | $schema: $schema,
|
367 | $id: $id,
|
368 | title: title,
|
369 | description: description,
|
370 | definitions: definitions,
|
371 | type: type,
|
372 | additionalProperties: additionalProperties,
|
373 | properties: properties,
|
374 | required: required
|
375 | };
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 | function versionComparator(versionA, versionB) {
|
384 | if (versionA.major < versionB.major) {
|
385 | return -1;
|
386 | } else if (versionA.major > versionB.major) {
|
387 | return 1;
|
388 | } else if (versionA.minor < versionB.minor) {
|
389 | return -1;
|
390 | } else if (versionA.minor > versionB.minor) {
|
391 | return 1;
|
392 | } else if (versionA.patch < versionB.patch) {
|
393 | return -1;
|
394 | } else if (versionA.patch > versionB.patch) {
|
395 | return 1;
|
396 | } else {
|
397 | return 0;
|
398 | }
|
399 | }
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 | function isVersionUpdate(base, update) {
|
406 | return versionComparator(base, update) < 0;
|
407 | }
|
408 |
|
409 | var VersionUpgrade;
|
410 |
|
411 | (function (VersionUpgrade) {
|
412 | VersionUpgrade[VersionUpgrade["NONE"] = 0] = "NONE";
|
413 | VersionUpgrade[VersionUpgrade["PATCH"] = 1] = "PATCH";
|
414 | VersionUpgrade[VersionUpgrade["MINOR"] = 2] = "MINOR";
|
415 | VersionUpgrade[VersionUpgrade["MAJOR"] = 3] = "MAJOR";
|
416 | })(VersionUpgrade || (VersionUpgrade = {}));
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 |
|
423 |
|
424 |
|
425 | function getVersionUpgrade(base, update) {
|
426 | if (update.major > base.major) {
|
427 | return VersionUpgrade.MAJOR;
|
428 | }
|
429 |
|
430 | if (update.major < base.major) {
|
431 | return VersionUpgrade.NONE;
|
432 | }
|
433 |
|
434 | if (update.minor > base.minor) {
|
435 | return VersionUpgrade.MINOR;
|
436 | }
|
437 |
|
438 | if (update.minor < base.minor) {
|
439 | return VersionUpgrade.NONE;
|
440 | }
|
441 |
|
442 | return update.patch > base.patch ? VersionUpgrade.PATCH : VersionUpgrade.NONE;
|
443 | }
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 | function compareTokenInfoProperty(a, b) {
|
452 | if (a === b) return true;
|
453 | if (typeof a !== typeof b) return false;
|
454 |
|
455 | if (Array.isArray(a) && Array.isArray(b)) {
|
456 | return a.every(function (el, i) {
|
457 | return b[i] === el;
|
458 | });
|
459 | }
|
460 |
|
461 | return false;
|
462 | }
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 |
|
469 |
|
470 | function diffTokenLists(base, update) {
|
471 | var indexedBase = base.reduce(function (memo, tokenInfo) {
|
472 | if (!memo[tokenInfo.chainId]) memo[tokenInfo.chainId] = {};
|
473 | memo[tokenInfo.chainId][tokenInfo.address] = tokenInfo;
|
474 | return memo;
|
475 | }, {});
|
476 | var newListUpdates = update.reduce(function (memo, tokenInfo) {
|
477 | var _indexedBase$tokenInf;
|
478 |
|
479 | var baseToken = (_indexedBase$tokenInf = indexedBase[tokenInfo.chainId]) == null ? void 0 : _indexedBase$tokenInf[tokenInfo.address];
|
480 |
|
481 | if (!baseToken) {
|
482 | memo.added.push(tokenInfo);
|
483 | } else {
|
484 | var changes = Object.keys(tokenInfo).filter(function (s) {
|
485 | return s !== 'address' && s !== 'chainId';
|
486 | }).filter(function (s) {
|
487 | return !compareTokenInfoProperty(tokenInfo[s], baseToken[s]);
|
488 | });
|
489 |
|
490 | if (changes.length > 0) {
|
491 | if (!memo.changed[tokenInfo.chainId]) {
|
492 | memo.changed[tokenInfo.chainId] = {};
|
493 | }
|
494 |
|
495 | memo.changed[tokenInfo.chainId][tokenInfo.address] = changes;
|
496 | }
|
497 | }
|
498 |
|
499 | if (!memo.index[tokenInfo.chainId]) {
|
500 | var _memo$index$tokenInfo;
|
501 |
|
502 | memo.index[tokenInfo.chainId] = (_memo$index$tokenInfo = {}, _memo$index$tokenInfo[tokenInfo.address] = true, _memo$index$tokenInfo);
|
503 | } else {
|
504 | memo.index[tokenInfo.chainId][tokenInfo.address] = true;
|
505 | }
|
506 |
|
507 | return memo;
|
508 | }, {
|
509 | added: [],
|
510 | changed: {},
|
511 | index: {}
|
512 | });
|
513 | var removed = base.reduce(function (list, curr) {
|
514 | if (!newListUpdates.index[curr.chainId] || !newListUpdates.index[curr.chainId][curr.address]) {
|
515 | list.push(curr);
|
516 | }
|
517 |
|
518 | return list;
|
519 | }, []);
|
520 | return {
|
521 | added: newListUpdates.added,
|
522 | changed: newListUpdates.changed,
|
523 | removed: removed
|
524 | };
|
525 | }
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 | function minVersionBump(baseList, updatedList) {
|
534 | var diff = diffTokenLists(baseList, updatedList);
|
535 | if (diff.removed.length > 0) return VersionUpgrade.MAJOR;
|
536 | if (diff.added.length > 0) return VersionUpgrade.MINOR;
|
537 | if (Object.keys(diff.changed).length > 0) return VersionUpgrade.PATCH;
|
538 | return VersionUpgrade.NONE;
|
539 | }
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 | function nextVersion(base, bump) {
|
548 | switch (bump) {
|
549 | case VersionUpgrade.NONE:
|
550 | return base;
|
551 |
|
552 | case VersionUpgrade.MAJOR:
|
553 | return {
|
554 | major: base.major + 1,
|
555 | minor: 0,
|
556 | patch: 0
|
557 | };
|
558 |
|
559 | case VersionUpgrade.MINOR:
|
560 | return {
|
561 | major: base.major,
|
562 | minor: base.minor + 1,
|
563 | patch: 0
|
564 | };
|
565 |
|
566 | case VersionUpgrade.PATCH:
|
567 | return {
|
568 | major: base.major,
|
569 | minor: base.minor,
|
570 | patch: base.patch + 1
|
571 | };
|
572 | }
|
573 | }
|
574 |
|
575 | export { VersionUpgrade, diffTokenLists, getVersionUpgrade, isVersionUpdate, minVersionBump, nextVersion, tokenlist_schema as schema, versionComparator };
|
576 |
|