@pancakeswap/token-lists 0.0.17 → 0.1.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.
- package/.turbo/turbo-build.log +13 -13
- package/CHANGELOG.md +19 -0
- package/dist/{chunk-DC45HQOL.mjs → chunk-UUVLERWJ.mjs} +1 -60
- package/dist/{chunk-YOAEDKQH.js → chunk-Y2NTTLTJ.js} +0 -62
- package/dist/index.d.ts +35 -3
- package/dist/index.js +74 -15
- package/dist/index.mjs +72 -1
- package/dist/react.d.ts +51 -10
- package/dist/react.js +325 -92
- package/dist/react.mjs +311 -85
- package/dist/{types-92a8b029.d.ts → types-dcc7ec4a.d.ts} +1 -0
- package/package.json +4 -3
- package/react/actions.ts +11 -0
- package/react/geoBlock.test.ts +68 -0
- package/react/getTokenList.test.ts +54 -0
- package/react/getTokenList.ts +2 -3
- package/react/index.test.ts +6 -0
- package/react/lists.ts +307 -27
- package/react/reducer.ts +34 -53
- package/react/reducerHelpers.ts +111 -0
- package/react/useFetchListCallback.ts +1 -1
- package/schema/pancakeswap.json +18 -9
- package/src/index.test.ts +1 -0
- package/src/index.ts +1 -0
- package/src/scaledUIAmount.ts +41 -0
- package/src/types.ts +1 -0
- package/src/wrappedTokenInfo.ts +8 -0
package/dist/react.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { __async, __spreadProps, __spreadValues, getVersionUpgrade } from './chunk-
|
|
1
|
+
import { __async, __spreadProps, __spreadValues, getVersionUpgrade } from './chunk-UUVLERWJ.mjs';
|
|
2
2
|
import { createAction, nanoid, createReducer } from '@reduxjs/toolkit';
|
|
3
3
|
import { atom, useAtom, useAtomValue } from 'jotai';
|
|
4
4
|
import { atomWithStorage, loadable, atomFamily } from 'jotai/utils';
|
|
5
5
|
import localForage from 'localforage';
|
|
6
|
+
import debounce from 'lodash/debounce';
|
|
6
7
|
import { useCallback } from 'react';
|
|
7
8
|
|
|
8
9
|
var fetchTokenList = {
|
|
@@ -17,6 +18,11 @@ var disableList = createAction("lists/disableList");
|
|
|
17
18
|
var acceptListUpdate = createAction("lists/acceptListUpdate");
|
|
18
19
|
var rejectVersionUpdate = createAction("lists/rejectVersionUpdate");
|
|
19
20
|
var updateListVersion = createAction("lists/updateListVersion");
|
|
21
|
+
var batchFetchTokenListPending = createAction(
|
|
22
|
+
"lists/batchFetchTokenList/pending"
|
|
23
|
+
);
|
|
24
|
+
var batchFetchTokenListFulfilled = createAction("lists/batchFetchTokenList/fulfilled");
|
|
25
|
+
var batchFetchTokenListRejected = createAction("lists/batchFetchTokenList/rejected");
|
|
20
26
|
|
|
21
27
|
// ../utils/uriToHttp.ts
|
|
22
28
|
function uriToHttp(uri) {
|
|
@@ -52,7 +58,7 @@ var pancakeswap_default = {
|
|
|
52
58
|
{
|
|
53
59
|
major: 1,
|
|
54
60
|
minor: 0,
|
|
55
|
-
patch:
|
|
61
|
+
patch: 1
|
|
56
62
|
}
|
|
57
63
|
],
|
|
58
64
|
additionalProperties: false,
|
|
@@ -237,17 +243,17 @@ var pancakeswap_default = {
|
|
|
237
243
|
type: "string",
|
|
238
244
|
description: "The name of the token",
|
|
239
245
|
minLength: 1,
|
|
240
|
-
maxLength:
|
|
241
|
-
pattern: "^[ \\w.'
|
|
242
|
-
examples: ["USD Coin"]
|
|
246
|
+
maxLength: 60,
|
|
247
|
+
pattern: "^[ \\w.'+\\-%/,\\$\xC0-\xD6\xD8-\xF6\xF8-\xFF:&\\[\\]\\(\\)\\u4e00-\\u9fa5]+$",
|
|
248
|
+
examples: ["USD Coin", "\u5E01\u5B89\u5E01"]
|
|
243
249
|
},
|
|
244
250
|
symbol: {
|
|
245
251
|
type: "string",
|
|
246
252
|
description: "The symbol for the token; must be alphanumeric",
|
|
247
|
-
pattern: "^[a-zA-Z0-9+\\-%/$.\\
|
|
253
|
+
pattern: "^[a-zA-Z0-9+\\-%/$.\\s_\\u4e00-\\u9fa5]+$",
|
|
248
254
|
minLength: 1,
|
|
249
255
|
maxLength: 20,
|
|
250
|
-
examples: ["USDC"]
|
|
256
|
+
examples: ["USDC", "\u5E01\u5B89"]
|
|
251
257
|
},
|
|
252
258
|
logoURI: {
|
|
253
259
|
type: "string",
|
|
@@ -297,14 +303,14 @@ var pancakeswap_default = {
|
|
|
297
303
|
type: "string",
|
|
298
304
|
description: "The name of the token",
|
|
299
305
|
minLength: 1,
|
|
300
|
-
maxLength:
|
|
301
|
-
pattern: "^[ \\w.'
|
|
306
|
+
maxLength: 60,
|
|
307
|
+
pattern: "^[ \\w.'+\\-%/,\\$\xC0-\xD6\xD8-\xF6\xF8-\xFF:&\\[\\]\\(\\)]+$",
|
|
302
308
|
examples: ["USD Coin"]
|
|
303
309
|
},
|
|
304
310
|
symbol: {
|
|
305
311
|
type: "string",
|
|
306
312
|
description: "The symbol for the token; must be alphanumeric",
|
|
307
|
-
pattern: "^[a-zA-Z0-9+\\-%/$.]+$",
|
|
313
|
+
pattern: "^[a-zA-Z0-9+\\-%/$._]+$",
|
|
308
314
|
minLength: 1,
|
|
309
315
|
maxLength: 20,
|
|
310
316
|
examples: ["USDC"]
|
|
@@ -397,6 +403,15 @@ var pancakeswap_default = {
|
|
|
397
403
|
description: "A URI for the logo of the token list; prefer SVG or PNG of size 256x256",
|
|
398
404
|
format: "uri",
|
|
399
405
|
examples: ["ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM"]
|
|
406
|
+
},
|
|
407
|
+
extensions: {
|
|
408
|
+
type: "object",
|
|
409
|
+
description: "An object containing arbitrary or vendor-specific token list metadata",
|
|
410
|
+
propertyNames: {
|
|
411
|
+
$ref: "#/definitions/ExtensionIdentifier"
|
|
412
|
+
},
|
|
413
|
+
additionalProperties: true,
|
|
414
|
+
maxProperties: 10
|
|
400
415
|
}
|
|
401
416
|
},
|
|
402
417
|
if: {
|
|
@@ -442,16 +457,15 @@ function getTokenList(listUrl) {
|
|
|
442
457
|
try {
|
|
443
458
|
const json = yield fetchJson(url);
|
|
444
459
|
if (!validator(json)) {
|
|
445
|
-
const preFilterErrors = validator.errors;
|
|
446
460
|
json.tokens = json.tokens.filter((token) => validator(__spreadProps(__spreadValues({}, json), { tokens: [token] })));
|
|
447
461
|
if (!validator(json)) {
|
|
448
|
-
const errors = validator
|
|
462
|
+
const { errors } = validator;
|
|
449
463
|
throw new Error(`Validation failed after filtering: ${JSON.stringify(errors)}`);
|
|
450
464
|
}
|
|
451
|
-
console.warn(`Pre-filter validation errors: ${JSON.stringify(preFilterErrors)}`);
|
|
452
465
|
}
|
|
453
466
|
return json;
|
|
454
467
|
} catch (error) {
|
|
468
|
+
console.warn(`Failed to download or validate list from ${url}:`, error);
|
|
455
469
|
return void 0;
|
|
456
470
|
}
|
|
457
471
|
}
|
|
@@ -466,6 +480,47 @@ function fetchJson(url) {
|
|
|
466
480
|
return res.json();
|
|
467
481
|
});
|
|
468
482
|
}
|
|
483
|
+
function normalizeTokenAddress(address) {
|
|
484
|
+
return address.startsWith("0x") ? address.toLowerCase() : address;
|
|
485
|
+
}
|
|
486
|
+
function getGeoBlockTokenKey(token) {
|
|
487
|
+
return `${token.chainId}:${normalizeTokenAddress(token.address)}`;
|
|
488
|
+
}
|
|
489
|
+
function normalizeTokenKey(key) {
|
|
490
|
+
const separatorIndex = key.indexOf(":");
|
|
491
|
+
if (separatorIndex < 0)
|
|
492
|
+
return key;
|
|
493
|
+
const chainId = key.slice(0, separatorIndex);
|
|
494
|
+
const address = key.slice(separatorIndex + 1);
|
|
495
|
+
return `${chainId}:${normalizeTokenAddress(address)}`;
|
|
496
|
+
}
|
|
497
|
+
function getGeoBlockedTokenKeys(geoBlockList, countryCode) {
|
|
498
|
+
const blockedTokenKeys = /* @__PURE__ */ new Set();
|
|
499
|
+
if (!countryCode || !(geoBlockList == null ? void 0 : geoBlockList.rules) || !geoBlockList.tokens) {
|
|
500
|
+
return blockedTokenKeys;
|
|
501
|
+
}
|
|
502
|
+
const country = countryCode.toUpperCase();
|
|
503
|
+
const blockedRuleIds = new Set(
|
|
504
|
+
Object.entries(geoBlockList.rules).filter(([, countries]) => countries.includes(country)).map(([ruleId]) => ruleId)
|
|
505
|
+
);
|
|
506
|
+
Object.entries(geoBlockList.tokens).forEach(([tokenKey, ruleIds]) => {
|
|
507
|
+
if (ruleIds.some((ruleId) => blockedRuleIds.has(ruleId))) {
|
|
508
|
+
blockedTokenKeys.add(normalizeTokenKey(tokenKey));
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
return blockedTokenKeys;
|
|
512
|
+
}
|
|
513
|
+
function isGeoBlockedToken(token, geoBlockList, countryCode) {
|
|
514
|
+
return getGeoBlockedTokenKeys(geoBlockList, countryCode).has(getGeoBlockTokenKey(token));
|
|
515
|
+
}
|
|
516
|
+
function fetchJson2(url) {
|
|
517
|
+
return __async(this, null, function* () {
|
|
518
|
+
const res = yield fetch(url);
|
|
519
|
+
if (!res.ok)
|
|
520
|
+
throw new Error(`Failed to fetch: ${url}`);
|
|
521
|
+
return res.json();
|
|
522
|
+
});
|
|
523
|
+
}
|
|
469
524
|
function noop() {
|
|
470
525
|
}
|
|
471
526
|
var noopStorage = {
|
|
@@ -496,82 +551,278 @@ function findTokenBySymbol(state, chainId, symbol) {
|
|
|
496
551
|
}
|
|
497
552
|
return void 0;
|
|
498
553
|
}
|
|
499
|
-
var createListsAtom = (storeName, reducer, initialState) => {
|
|
554
|
+
var createListsAtom = (storeName, reducer, initialState, options = {}) => {
|
|
500
555
|
function IndexedDBStorage(dbName) {
|
|
501
556
|
if (typeof window !== "undefined") {
|
|
502
557
|
const db = localForage.createInstance({
|
|
503
558
|
name: dbName,
|
|
504
559
|
storeName
|
|
505
560
|
});
|
|
561
|
+
const mem = /* @__PURE__ */ new Map();
|
|
562
|
+
const debouncedSetItem = debounce((k, v) => __async(this, null, function* () {
|
|
563
|
+
db.setItem(k, v);
|
|
564
|
+
}), 300);
|
|
506
565
|
return {
|
|
507
566
|
getItem: (key) => __async(this, null, function* () {
|
|
567
|
+
if (mem.has(key)) {
|
|
568
|
+
return mem.get(key);
|
|
569
|
+
}
|
|
508
570
|
const value = yield db.getItem(key);
|
|
509
571
|
if (value) {
|
|
510
572
|
return value;
|
|
511
573
|
}
|
|
512
|
-
return
|
|
574
|
+
return void 0;
|
|
513
575
|
}),
|
|
514
576
|
setItem: (k, v) => __async(this, null, function* () {
|
|
515
577
|
if (v === EMPTY)
|
|
516
578
|
return;
|
|
517
|
-
|
|
579
|
+
mem.set(k, v);
|
|
580
|
+
debouncedSetItem(k, v);
|
|
518
581
|
}),
|
|
519
582
|
removeItem: db.removeItem
|
|
520
583
|
};
|
|
521
584
|
}
|
|
522
585
|
return noopStorage;
|
|
523
586
|
}
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
return value.data;
|
|
530
|
-
}
|
|
531
|
-
return initialState;
|
|
532
|
-
},
|
|
533
|
-
(get, set, action) => __async(void 0, null, function* () {
|
|
534
|
-
set(listsStorageAtom, reducer(yield get(defaultStateAtom), action));
|
|
535
|
-
})
|
|
587
|
+
const tokenListsStorageAtom = atomWithStorage(
|
|
588
|
+
"tokenLists",
|
|
589
|
+
EMPTY,
|
|
590
|
+
IndexedDBStorage("tokenLists"),
|
|
591
|
+
{ getOnInit: true }
|
|
536
592
|
);
|
|
537
|
-
const
|
|
593
|
+
const geoBlockListStorageAtom = atomWithStorage(
|
|
594
|
+
"geoBlockList",
|
|
595
|
+
EMPTY,
|
|
596
|
+
IndexedDBStorage("geoBlockList")
|
|
597
|
+
);
|
|
598
|
+
const geoBlockListAtom = atom((get) => {
|
|
599
|
+
const value = get(loadable(geoBlockListStorageAtom));
|
|
600
|
+
if (value.state === "hasData" && value.data && value.data !== EMPTY) {
|
|
601
|
+
return value.data;
|
|
602
|
+
}
|
|
603
|
+
return void 0;
|
|
604
|
+
});
|
|
605
|
+
const fetchGeoBlockListAtom = atom(null, (_get, set) => __async(void 0, null, function* () {
|
|
606
|
+
if (!options.geoBlockListUrl) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
try {
|
|
610
|
+
set(geoBlockListStorageAtom, yield fetchJson2(options.geoBlockListUrl));
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.warn(`Failed to download geo block list from ${options.geoBlockListUrl}:`, error);
|
|
613
|
+
}
|
|
614
|
+
}));
|
|
615
|
+
const memoryStateAtom = atom(initialState);
|
|
616
|
+
const mergeStoredData = (baseState, storedData) => {
|
|
617
|
+
var _a;
|
|
618
|
+
const storedTokenLists = (_a = storedData.byUrl) != null ? _a : storedData;
|
|
619
|
+
const storedActiveListUrls = Array.isArray(storedData.activeListUrls) ? storedData.activeListUrls : void 0;
|
|
620
|
+
const mergedState = __spreadValues({}, baseState);
|
|
621
|
+
const updatedByUrl = __spreadValues({}, mergedState.byUrl);
|
|
622
|
+
Object.keys(storedTokenLists).forEach((url) => {
|
|
623
|
+
if (storedTokenLists[url]) {
|
|
624
|
+
updatedByUrl[url] = __spreadProps(__spreadValues({}, updatedByUrl[url]), {
|
|
625
|
+
current: storedTokenLists[url]
|
|
626
|
+
// Keep existing memory state for loading, error, pendingUpdate
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
mergedState.byUrl = updatedByUrl;
|
|
631
|
+
if (storedActiveListUrls) {
|
|
632
|
+
mergedState.activeListUrls = storedActiveListUrls;
|
|
633
|
+
}
|
|
634
|
+
return mergedState;
|
|
635
|
+
};
|
|
636
|
+
const listStateAtom = atom((get) => {
|
|
637
|
+
const memoryState = get(memoryStateAtom);
|
|
638
|
+
const value = get(loadable(tokenListsStorageAtom));
|
|
639
|
+
if (value.state === "hasData" && value.data && value.data !== EMPTY) {
|
|
640
|
+
return mergeStoredData(memoryState, value.data);
|
|
641
|
+
}
|
|
642
|
+
return memoryState;
|
|
643
|
+
});
|
|
644
|
+
let hasHydratedActiveListUrls = false;
|
|
645
|
+
const updateListStateAtom = atom(null, (get, set, action) => __async(void 0, null, function* () {
|
|
646
|
+
const baseState = get(memoryStateAtom);
|
|
647
|
+
if (!hasHydratedActiveListUrls) {
|
|
648
|
+
const storageValue = get(loadable(tokenListsStorageAtom));
|
|
649
|
+
if (storageValue.state !== "hasData") {
|
|
650
|
+
const optimisticState = reducer(baseState, action);
|
|
651
|
+
set(memoryStateAtom, __spreadValues({}, optimisticState));
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (storageValue.data && storageValue.data !== EMPTY) {
|
|
655
|
+
set(memoryStateAtom, mergeStoredData(baseState, storageValue.data));
|
|
656
|
+
}
|
|
657
|
+
hasHydratedActiveListUrls = true;
|
|
658
|
+
}
|
|
659
|
+
const currentState = get(memoryStateAtom);
|
|
660
|
+
const newState = reducer(currentState, action);
|
|
661
|
+
set(memoryStateAtom, __spreadValues({}, newState));
|
|
662
|
+
const tokenListsToStore = {};
|
|
663
|
+
Object.keys(newState.byUrl).forEach((url) => {
|
|
664
|
+
var _a;
|
|
665
|
+
if ((_a = newState.byUrl[url]) == null ? void 0 : _a.current) {
|
|
666
|
+
tokenListsToStore[url] = newState.byUrl[url].current;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
set(tokenListsStorageAtom, { byUrl: tokenListsToStore, activeListUrls: newState.activeListUrls });
|
|
670
|
+
}));
|
|
671
|
+
const isReadyAtom = loadable(tokenListsStorageAtom);
|
|
538
672
|
const tokenAtom = atomFamily(
|
|
539
|
-
(key) => atom((get) => findTokenByAddress(get(
|
|
673
|
+
(key) => atom((get) => findTokenByAddress(get(listStateAtom), key.chainId, key.address))
|
|
540
674
|
);
|
|
541
675
|
const tokenBySymbolAtom = atomFamily(
|
|
542
|
-
(key) => atom((get) => findTokenBySymbol(get(
|
|
676
|
+
(key) => atom((get) => findTokenBySymbol(get(listStateAtom), key.chainId, key.symbol))
|
|
543
677
|
);
|
|
544
678
|
const fetchListAtom = atom(null, (get, set, url) => __async(void 0, null, function* () {
|
|
545
|
-
const state = get(
|
|
679
|
+
const state = get(listStateAtom);
|
|
546
680
|
const listState = state.byUrl[url];
|
|
547
681
|
if ((listState == null ? void 0 : listState.current) || (listState == null ? void 0 : listState.loadingRequestId)) {
|
|
548
682
|
return;
|
|
549
683
|
}
|
|
550
684
|
const requestId = nanoid();
|
|
551
|
-
set(
|
|
685
|
+
set(updateListStateAtom, fetchTokenList.pending({ url, requestId }));
|
|
552
686
|
try {
|
|
553
687
|
const tokenList = yield getTokenList(url);
|
|
554
|
-
set(
|
|
688
|
+
set(updateListStateAtom, fetchTokenList.fulfilled({ url, tokenList, requestId }));
|
|
555
689
|
} catch (error) {
|
|
556
|
-
set(
|
|
690
|
+
set(updateListStateAtom, fetchTokenList.rejected({ url, requestId, errorMessage: error.message }));
|
|
691
|
+
}
|
|
692
|
+
}));
|
|
693
|
+
const fetchListBatchAtom = atom(null, (get, set, urls) => __async(void 0, null, function* () {
|
|
694
|
+
const state = get(listStateAtom);
|
|
695
|
+
const urlsToFetch = urls.filter((url) => {
|
|
696
|
+
const listState = state.byUrl[url];
|
|
697
|
+
return !(listState == null ? void 0 : listState.current) && !(listState == null ? void 0 : listState.loadingRequestId);
|
|
698
|
+
});
|
|
699
|
+
if (urlsToFetch.length === 0) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const requestId = nanoid();
|
|
703
|
+
set(updateListStateAtom, batchFetchTokenListPending({ urls: urlsToFetch, requestId }));
|
|
704
|
+
try {
|
|
705
|
+
const results = yield Promise.allSettled(
|
|
706
|
+
urlsToFetch.map((url) => __async(void 0, null, function* () {
|
|
707
|
+
const tokenList = yield getTokenList(url);
|
|
708
|
+
return { url, tokenList, requestId };
|
|
709
|
+
}))
|
|
710
|
+
);
|
|
711
|
+
const fulfilled = [];
|
|
712
|
+
const rejected = [];
|
|
713
|
+
results.forEach((result, index) => {
|
|
714
|
+
var _a;
|
|
715
|
+
const url = urlsToFetch[index];
|
|
716
|
+
if (result.status === "fulfilled") {
|
|
717
|
+
fulfilled.push(result.value);
|
|
718
|
+
} else {
|
|
719
|
+
rejected.push({
|
|
720
|
+
url,
|
|
721
|
+
errorMessage: ((_a = result.reason) == null ? void 0 : _a.message) || "Unknown error",
|
|
722
|
+
requestId
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
if (fulfilled.length > 0) {
|
|
727
|
+
set(updateListStateAtom, batchFetchTokenListFulfilled({ results: fulfilled }));
|
|
728
|
+
}
|
|
729
|
+
if (rejected.length > 0) {
|
|
730
|
+
set(updateListStateAtom, batchFetchTokenListRejected({ errors: rejected }));
|
|
731
|
+
}
|
|
732
|
+
} catch (error) {
|
|
733
|
+
const errors = urlsToFetch.map((url) => ({
|
|
734
|
+
url,
|
|
735
|
+
errorMessage: error.message,
|
|
736
|
+
requestId
|
|
737
|
+
}));
|
|
738
|
+
set(updateListStateAtom, batchFetchTokenListRejected({ errors }));
|
|
557
739
|
}
|
|
558
740
|
}));
|
|
559
741
|
function useListState() {
|
|
560
|
-
return useAtom(
|
|
742
|
+
return useAtom(listStateAtom);
|
|
561
743
|
}
|
|
562
744
|
function useListStateReady() {
|
|
563
745
|
const value = useAtomValue(isReadyAtom);
|
|
564
|
-
return value.state === "hasData"
|
|
746
|
+
return value.state === "hasData";
|
|
565
747
|
}
|
|
566
748
|
return {
|
|
567
|
-
listsAtom:
|
|
749
|
+
listsAtom: listStateAtom,
|
|
750
|
+
updateListStateAtom,
|
|
568
751
|
tokenAtom,
|
|
569
752
|
tokenBySymbolAtom,
|
|
753
|
+
geoBlockListAtom,
|
|
754
|
+
fetchGeoBlockListAtom,
|
|
570
755
|
fetchListAtom,
|
|
756
|
+
fetchListBatchAtom,
|
|
571
757
|
useListStateReady,
|
|
572
758
|
useListState
|
|
573
759
|
};
|
|
574
760
|
};
|
|
761
|
+
|
|
762
|
+
// react/reducerHelpers.ts
|
|
763
|
+
var setPendingTokenList = (state, url, requestId) => {
|
|
764
|
+
var _a, _b, _c, _d;
|
|
765
|
+
const current = (_b = (_a = state.byUrl[url]) == null ? void 0 : _a.current) != null ? _b : null;
|
|
766
|
+
const pendingUpdate = (_d = (_c = state.byUrl[url]) == null ? void 0 : _c.pendingUpdate) != null ? _d : null;
|
|
767
|
+
state.byUrl[url] = {
|
|
768
|
+
current,
|
|
769
|
+
pendingUpdate,
|
|
770
|
+
loadingRequestId: requestId,
|
|
771
|
+
error: null
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
var setFulfilledTokenList = (state, url, tokenList, requestId, DEFAULT_ACTIVE_LIST_URLS) => {
|
|
775
|
+
var _a, _b;
|
|
776
|
+
const current = (_a = state.byUrl[url]) == null ? void 0 : _a.current;
|
|
777
|
+
const loadingRequestId = (_b = state.byUrl[url]) == null ? void 0 : _b.loadingRequestId;
|
|
778
|
+
if (current) {
|
|
779
|
+
const upgradeType = getVersionUpgrade(current.version, tokenList.version);
|
|
780
|
+
if (upgradeType === 0 /* NONE */)
|
|
781
|
+
return false;
|
|
782
|
+
if (loadingRequestId === null || loadingRequestId === requestId) {
|
|
783
|
+
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
784
|
+
loadingRequestId: null,
|
|
785
|
+
error: null,
|
|
786
|
+
current,
|
|
787
|
+
pendingUpdate: tokenList
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
if (DEFAULT_ACTIVE_LIST_URLS.includes(url) && state.activeListUrls && !state.activeListUrls.includes(url)) {
|
|
793
|
+
state.activeListUrls.push(url);
|
|
794
|
+
}
|
|
795
|
+
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
796
|
+
loadingRequestId: null,
|
|
797
|
+
error: null,
|
|
798
|
+
current: tokenList,
|
|
799
|
+
pendingUpdate: null
|
|
800
|
+
});
|
|
801
|
+
return true;
|
|
802
|
+
};
|
|
803
|
+
var setRejectedTokenList = (state, url, requestId, errorMessage) => {
|
|
804
|
+
var _a;
|
|
805
|
+
if (((_a = state.byUrl[url]) == null ? void 0 : _a.loadingRequestId) !== requestId) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
809
|
+
loadingRequestId: null,
|
|
810
|
+
error: errorMessage,
|
|
811
|
+
current: null,
|
|
812
|
+
pendingUpdate: null
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
var batchActivateUrls = (state, urls) => {
|
|
816
|
+
if (urls.length > 0 && state.activeListUrls) {
|
|
817
|
+
urls.forEach((url) => {
|
|
818
|
+
if (!state.activeListUrls.includes(url)) {
|
|
819
|
+
state.activeListUrls.push(url);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
// react/reducer.ts
|
|
575
826
|
var NEW_LIST_STATE = {
|
|
576
827
|
error: null,
|
|
577
828
|
current: null,
|
|
@@ -581,53 +832,11 @@ var NEW_LIST_STATE = {
|
|
|
581
832
|
var createTokenListReducer = (initialState, DEFAULT_LIST_OF_LISTS, DEFAULT_ACTIVE_LIST_URLS) => createReducer(
|
|
582
833
|
initialState,
|
|
583
834
|
(builder) => builder.addCase(fetchTokenList.pending, (state, { payload: { requestId, url } }) => {
|
|
584
|
-
|
|
585
|
-
const current = (_b = (_a = state.byUrl[url]) == null ? void 0 : _a.current) != null ? _b : null;
|
|
586
|
-
const pendingUpdate = (_d = (_c = state.byUrl[url]) == null ? void 0 : _c.pendingUpdate) != null ? _d : null;
|
|
587
|
-
state.byUrl[url] = {
|
|
588
|
-
current,
|
|
589
|
-
pendingUpdate,
|
|
590
|
-
loadingRequestId: requestId,
|
|
591
|
-
error: null
|
|
592
|
-
};
|
|
835
|
+
setPendingTokenList(state, url, requestId);
|
|
593
836
|
}).addCase(fetchTokenList.fulfilled, (state, { payload: { requestId, tokenList, url } }) => {
|
|
594
|
-
|
|
595
|
-
const current = (_a = state.byUrl[url]) == null ? void 0 : _a.current;
|
|
596
|
-
const loadingRequestId = (_b = state.byUrl[url]) == null ? void 0 : _b.loadingRequestId;
|
|
597
|
-
if (current) {
|
|
598
|
-
const upgradeType = getVersionUpgrade(current.version, tokenList.version);
|
|
599
|
-
if (upgradeType === 0 /* NONE */)
|
|
600
|
-
return;
|
|
601
|
-
if (loadingRequestId === null || loadingRequestId === requestId) {
|
|
602
|
-
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
603
|
-
loadingRequestId: null,
|
|
604
|
-
error: null,
|
|
605
|
-
current,
|
|
606
|
-
pendingUpdate: tokenList
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
} else {
|
|
610
|
-
if (DEFAULT_ACTIVE_LIST_URLS.includes(url) && state.activeListUrls && !state.activeListUrls.includes(url)) {
|
|
611
|
-
state.activeListUrls.push(url);
|
|
612
|
-
}
|
|
613
|
-
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
614
|
-
loadingRequestId: null,
|
|
615
|
-
error: null,
|
|
616
|
-
current: tokenList,
|
|
617
|
-
pendingUpdate: null
|
|
618
|
-
});
|
|
619
|
-
}
|
|
837
|
+
setFulfilledTokenList(state, url, tokenList, requestId, DEFAULT_ACTIVE_LIST_URLS);
|
|
620
838
|
}).addCase(fetchTokenList.rejected, (state, { payload: { url, requestId, errorMessage } }) => {
|
|
621
|
-
|
|
622
|
-
if (((_a = state.byUrl[url]) == null ? void 0 : _a.loadingRequestId) !== requestId) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
state.byUrl[url] = __spreadProps(__spreadValues({}, state.byUrl[url]), {
|
|
626
|
-
loadingRequestId: null,
|
|
627
|
-
error: errorMessage,
|
|
628
|
-
current: null,
|
|
629
|
-
pendingUpdate: null
|
|
630
|
-
});
|
|
839
|
+
setRejectedTokenList(state, url, requestId, errorMessage);
|
|
631
840
|
}).addCase(addList, (state, { payload: url }) => {
|
|
632
841
|
if (!state.byUrl[url]) {
|
|
633
842
|
state.byUrl[url] = NEW_LIST_STATE;
|
|
@@ -693,6 +902,23 @@ var createTokenListReducer = (initialState, DEFAULT_LIST_OF_LISTS, DEFAULT_ACTIV
|
|
|
693
902
|
return true;
|
|
694
903
|
});
|
|
695
904
|
}
|
|
905
|
+
}).addCase(batchFetchTokenListPending, (state, { payload: { urls, requestId } }) => {
|
|
906
|
+
urls.forEach((url) => {
|
|
907
|
+
setPendingTokenList(state, url, requestId);
|
|
908
|
+
});
|
|
909
|
+
}).addCase(batchFetchTokenListFulfilled, (state, { payload: { results } }) => {
|
|
910
|
+
const urlsToActivate = [];
|
|
911
|
+
results.forEach(({ url, tokenList, requestId }) => {
|
|
912
|
+
const isNewList = setFulfilledTokenList(state, url, tokenList, requestId, DEFAULT_ACTIVE_LIST_URLS);
|
|
913
|
+
if (isNewList && DEFAULT_ACTIVE_LIST_URLS.includes(url)) {
|
|
914
|
+
urlsToActivate.push(url);
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
batchActivateUrls(state, urlsToActivate);
|
|
918
|
+
}).addCase(batchFetchTokenListRejected, (state, { payload: { errors } }) => {
|
|
919
|
+
errors.forEach(({ url, requestId, errorMessage }) => {
|
|
920
|
+
setRejectedTokenList(state, url, requestId, errorMessage);
|
|
921
|
+
});
|
|
696
922
|
})
|
|
697
923
|
);
|
|
698
924
|
function useFetchListCallback(dispatch) {
|
|
@@ -711,7 +937,7 @@ function useFetchListCallback(dispatch) {
|
|
|
711
937
|
}
|
|
712
938
|
return tokenList;
|
|
713
939
|
}).catch((error) => {
|
|
714
|
-
console.
|
|
940
|
+
console.warn(`Failed to get list at url ${listUrl}`, error);
|
|
715
941
|
if (sendDispatch) {
|
|
716
942
|
dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message }));
|
|
717
943
|
}
|
|
@@ -723,4 +949,4 @@ function useFetchListCallback(dispatch) {
|
|
|
723
949
|
}
|
|
724
950
|
var useFetchListCallback_default = useFetchListCallback;
|
|
725
951
|
|
|
726
|
-
export { NEW_LIST_STATE, acceptListUpdate, addList, createListsAtom, createTokenListReducer, disableList, enableList, fetchTokenList, findTokenByAddress, findTokenBySymbol, getTokenList, rejectVersionUpdate, removeList, updateListVersion, useFetchListCallback_default as useFetchListCallback };
|
|
952
|
+
export { NEW_LIST_STATE, acceptListUpdate, addList, batchFetchTokenListFulfilled, batchFetchTokenListPending, batchFetchTokenListRejected, createListsAtom, createTokenListReducer, disableList, enableList, fetchTokenList, findTokenByAddress, findTokenBySymbol, getGeoBlockTokenKey, getGeoBlockedTokenKeys, getTokenList, isGeoBlockedToken, rejectVersionUpdate, removeList, updateListVersion, useFetchListCallback_default as useFetchListCallback };
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pancakeswap/token-lists",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"ajv": "^6.12.3",
|
|
9
9
|
"lodash": "^4.17.21",
|
|
10
|
-
"@pancakeswap/swap-sdk-core": "1.
|
|
10
|
+
"@pancakeswap/swap-sdk-core": "1.6.1"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@reduxjs/toolkit": "^1.9.1",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"localforage": "^1.10.0",
|
|
23
23
|
"react": "^18.2.0",
|
|
24
24
|
"tsup": "^6.7.0",
|
|
25
|
-
"@
|
|
25
|
+
"@types/lodash": "^4.14.182",
|
|
26
|
+
"@pancakeswap/utils": "8.0.3"
|
|
26
27
|
},
|
|
27
28
|
"exports": {
|
|
28
29
|
".": {
|
package/react/actions.ts
CHANGED
|
@@ -23,3 +23,14 @@ export const acceptListUpdate = createAction<string>('lists/acceptListUpdate')
|
|
|
23
23
|
export const rejectVersionUpdate = createAction<Version>('lists/rejectVersionUpdate')
|
|
24
24
|
|
|
25
25
|
export const updateListVersion = createAction('lists/updateListVersion')
|
|
26
|
+
|
|
27
|
+
// Batch actions for reducing update cycles
|
|
28
|
+
export const batchFetchTokenListPending = createAction<{ urls: string[]; requestId: string }>(
|
|
29
|
+
'lists/batchFetchTokenList/pending',
|
|
30
|
+
)
|
|
31
|
+
export const batchFetchTokenListFulfilled = createAction<{
|
|
32
|
+
results: Array<{ url: string; tokenList: TokenList; requestId: string }>
|
|
33
|
+
}>('lists/batchFetchTokenList/fulfilled')
|
|
34
|
+
export const batchFetchTokenListRejected = createAction<{
|
|
35
|
+
errors: Array<{ url: string; errorMessage: string; requestId: string }>
|
|
36
|
+
}>('lists/batchFetchTokenList/rejected')
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getGeoBlockedTokenKeys, getGeoBlockTokenKey, isGeoBlockedToken } from './lists'
|
|
3
|
+
|
|
4
|
+
const GEO_BLOCK_LIST = {
|
|
5
|
+
rules: {
|
|
6
|
+
A: ['US'],
|
|
7
|
+
B: ['SG'],
|
|
8
|
+
},
|
|
9
|
+
tokens: {
|
|
10
|
+
'56:0xa0Fe4e0aEca5479705ce996615B2EACB6b6a10Fb': ['A'],
|
|
11
|
+
'8000001001:Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh': ['A'],
|
|
12
|
+
'56:0x0000000000000000000000000000000000000001': ['B'],
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('geo block helpers', () => {
|
|
17
|
+
it('normalizes EVM addresses and preserves non-EVM addresses', () => {
|
|
18
|
+
expect(
|
|
19
|
+
getGeoBlockTokenKey({
|
|
20
|
+
chainId: 56,
|
|
21
|
+
address: '0xa0Fe4e0aEca5479705ce996615B2EACB6b6a10Fb',
|
|
22
|
+
}),
|
|
23
|
+
).toBe('56:0xa0fe4e0aeca5479705ce996615b2eacb6b6a10fb')
|
|
24
|
+
|
|
25
|
+
expect(
|
|
26
|
+
getGeoBlockTokenKey({
|
|
27
|
+
chainId: 8000001001,
|
|
28
|
+
address: 'Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh',
|
|
29
|
+
}),
|
|
30
|
+
).toBe('8000001001:Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('returns blocked token keys for the active country', () => {
|
|
34
|
+
expect([...getGeoBlockedTokenKeys(GEO_BLOCK_LIST, 'US')].sort()).toEqual([
|
|
35
|
+
'56:0xa0fe4e0aeca5479705ce996615b2eacb6b6a10fb',
|
|
36
|
+
'8000001001:Xsc9qvGR1efVDFGLrVsmkzv3qi45LTBjeUKSPmx9qEh',
|
|
37
|
+
])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('fails open without country or geo block list', () => {
|
|
41
|
+
expect(getGeoBlockedTokenKeys(GEO_BLOCK_LIST).size).toBe(0)
|
|
42
|
+
expect(getGeoBlockedTokenKeys(undefined, 'US').size).toBe(0)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('checks whether a token is geo blocked', () => {
|
|
46
|
+
expect(
|
|
47
|
+
isGeoBlockedToken(
|
|
48
|
+
{
|
|
49
|
+
chainId: 56,
|
|
50
|
+
address: '0xa0Fe4e0aEca5479705ce996615B2EACB6b6a10Fb',
|
|
51
|
+
},
|
|
52
|
+
GEO_BLOCK_LIST,
|
|
53
|
+
'US',
|
|
54
|
+
),
|
|
55
|
+
).toBe(true)
|
|
56
|
+
|
|
57
|
+
expect(
|
|
58
|
+
isGeoBlockedToken(
|
|
59
|
+
{
|
|
60
|
+
chainId: 56,
|
|
61
|
+
address: '0xa0Fe4e0aEca5479705ce996615B2EACB6b6a10Fb',
|
|
62
|
+
},
|
|
63
|
+
GEO_BLOCK_LIST,
|
|
64
|
+
'SG',
|
|
65
|
+
),
|
|
66
|
+
).toBe(false)
|
|
67
|
+
})
|
|
68
|
+
})
|