@pancakeswap/token-lists 0.0.2

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/dist/react.mjs ADDED
@@ -0,0 +1,249 @@
1
+ import {
2
+ getVersionUpgrade
3
+ } from "./chunk-YBVZDUCZ.mjs";
4
+
5
+ // react/reducer.ts
6
+ import { createReducer } from "@reduxjs/toolkit";
7
+
8
+ // react/actions.ts
9
+ import { createAction } from "@reduxjs/toolkit";
10
+ var fetchTokenList = {
11
+ pending: createAction("lists/fetchTokenList/pending"),
12
+ fulfilled: createAction("lists/fetchTokenList/fulfilled"),
13
+ rejected: createAction("lists/fetchTokenList/rejected")
14
+ };
15
+ var addList = createAction("lists/addList");
16
+ var removeList = createAction("lists/removeList");
17
+ var enableList = createAction("lists/enableList");
18
+ var disableList = createAction("lists/disableList");
19
+ var acceptListUpdate = createAction("lists/acceptListUpdate");
20
+ var rejectVersionUpdate = createAction("lists/rejectVersionUpdate");
21
+ var updateListVersion = createAction("lists/updateListVersion");
22
+
23
+ // react/reducer.ts
24
+ var NEW_LIST_STATE = {
25
+ error: null,
26
+ current: null,
27
+ loadingRequestId: null,
28
+ pendingUpdate: null
29
+ };
30
+ var createTokenListReducer = (initialState, DEFAULT_LIST_OF_LISTS, DEFAULT_ACTIVE_LIST_URLS) => createReducer(
31
+ initialState,
32
+ (builder) => builder.addCase(fetchTokenList.pending, (state, { payload: { requestId, url } }) => {
33
+ var _a, _b;
34
+ const current = ((_a = state.byUrl[url]) == null ? void 0 : _a.current) ?? null;
35
+ const pendingUpdate = ((_b = state.byUrl[url]) == null ? void 0 : _b.pendingUpdate) ?? null;
36
+ state.byUrl[url] = {
37
+ current,
38
+ pendingUpdate,
39
+ loadingRequestId: requestId,
40
+ error: null
41
+ };
42
+ }).addCase(fetchTokenList.fulfilled, (state, { payload: { requestId, tokenList, url } }) => {
43
+ var _a, _b, _c;
44
+ const current = (_a = state.byUrl[url]) == null ? void 0 : _a.current;
45
+ const loadingRequestId = (_b = state.byUrl[url]) == null ? void 0 : _b.loadingRequestId;
46
+ if (current) {
47
+ const upgradeType = getVersionUpgrade(current.version, tokenList.version);
48
+ if (upgradeType === 0 /* NONE */)
49
+ return;
50
+ if (loadingRequestId === null || loadingRequestId === requestId) {
51
+ state.byUrl[url] = {
52
+ ...state.byUrl[url],
53
+ loadingRequestId: null,
54
+ error: null,
55
+ current,
56
+ pendingUpdate: tokenList
57
+ };
58
+ }
59
+ } else {
60
+ if (DEFAULT_ACTIVE_LIST_URLS.includes(url)) {
61
+ (_c = state.activeListUrls) == null ? void 0 : _c.push(url);
62
+ }
63
+ state.byUrl[url] = {
64
+ ...state.byUrl[url],
65
+ loadingRequestId: null,
66
+ error: null,
67
+ current: tokenList,
68
+ pendingUpdate: null
69
+ };
70
+ }
71
+ }).addCase(fetchTokenList.rejected, (state, { payload: { url, requestId, errorMessage } }) => {
72
+ var _a;
73
+ if (((_a = state.byUrl[url]) == null ? void 0 : _a.loadingRequestId) !== requestId) {
74
+ return;
75
+ }
76
+ state.byUrl[url] = {
77
+ ...state.byUrl[url],
78
+ loadingRequestId: null,
79
+ error: errorMessage,
80
+ current: null,
81
+ pendingUpdate: null
82
+ };
83
+ }).addCase(addList, (state, { payload: url }) => {
84
+ if (!state.byUrl[url]) {
85
+ state.byUrl[url] = NEW_LIST_STATE;
86
+ }
87
+ }).addCase(removeList, (state, { payload: url }) => {
88
+ if (state.byUrl[url]) {
89
+ delete state.byUrl[url];
90
+ }
91
+ if (state.activeListUrls && state.activeListUrls.includes(url)) {
92
+ state.activeListUrls = state.activeListUrls.filter((u) => u !== url);
93
+ }
94
+ }).addCase(enableList, (state, { payload: url }) => {
95
+ if (!state.byUrl[url]) {
96
+ state.byUrl[url] = NEW_LIST_STATE;
97
+ }
98
+ if (state.activeListUrls && !state.activeListUrls.includes(url)) {
99
+ state.activeListUrls.push(url);
100
+ }
101
+ if (!state.activeListUrls) {
102
+ state.activeListUrls = [url];
103
+ }
104
+ }).addCase(disableList, (state, { payload: url }) => {
105
+ if (state.activeListUrls && state.activeListUrls.includes(url)) {
106
+ state.activeListUrls = state.activeListUrls.filter((u) => u !== url);
107
+ }
108
+ }).addCase(acceptListUpdate, (state, { payload: url }) => {
109
+ var _a;
110
+ if (!((_a = state.byUrl[url]) == null ? void 0 : _a.pendingUpdate)) {
111
+ throw new Error("accept list update called without pending update");
112
+ }
113
+ state.byUrl[url] = {
114
+ ...state.byUrl[url],
115
+ pendingUpdate: null,
116
+ current: state.byUrl[url].pendingUpdate
117
+ };
118
+ }).addCase(updateListVersion, (state) => {
119
+ if (!state.lastInitializedDefaultListOfLists) {
120
+ state.byUrl = initialState.byUrl;
121
+ state.activeListUrls = initialState.activeListUrls;
122
+ } else if (state.lastInitializedDefaultListOfLists) {
123
+ const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce(
124
+ (s, l) => s.add(l),
125
+ /* @__PURE__ */ new Set()
126
+ );
127
+ const newListOfListsSet = DEFAULT_LIST_OF_LISTS.reduce((s, l) => s.add(l), /* @__PURE__ */ new Set());
128
+ DEFAULT_LIST_OF_LISTS.forEach((listUrl) => {
129
+ if (!lastInitializedSet.has(listUrl)) {
130
+ state.byUrl[listUrl] = NEW_LIST_STATE;
131
+ }
132
+ });
133
+ state.lastInitializedDefaultListOfLists.forEach((listUrl) => {
134
+ if (!newListOfListsSet.has(listUrl)) {
135
+ delete state.byUrl[listUrl];
136
+ }
137
+ });
138
+ }
139
+ state.lastInitializedDefaultListOfLists = DEFAULT_LIST_OF_LISTS;
140
+ if (!state.activeListUrls) {
141
+ state.activeListUrls = DEFAULT_ACTIVE_LIST_URLS;
142
+ DEFAULT_ACTIVE_LIST_URLS.forEach((listUrl) => {
143
+ if (!state.byUrl[listUrl]) {
144
+ state.byUrl[listUrl] = NEW_LIST_STATE;
145
+ }
146
+ return true;
147
+ });
148
+ }
149
+ })
150
+ );
151
+
152
+ // react/lists.ts
153
+ import { atomWithStorage, useReducerAtom } from "jotai/utils";
154
+ import localForage from "localforage";
155
+ function noop() {
156
+ }
157
+ var noopStorage = {
158
+ getItem: noop,
159
+ setItem: noop,
160
+ removeItem: noop,
161
+ keys: [],
162
+ getAllKeys: noop
163
+ };
164
+ var createListsAtom = (storeName, reducer, initialState) => {
165
+ let gotOnce = false;
166
+ function IndexedDBStorage(dbName) {
167
+ if (typeof window !== "undefined") {
168
+ const db = localForage.createInstance({
169
+ name: dbName,
170
+ storeName
171
+ });
172
+ return {
173
+ db,
174
+ getItem: async (key) => {
175
+ const value = await db.getItem(key);
176
+ gotOnce = true;
177
+ if (value) {
178
+ return value;
179
+ }
180
+ return initialState;
181
+ },
182
+ setItem: (k, v) => {
183
+ if (gotOnce) {
184
+ return db.setItem(k, v);
185
+ }
186
+ return void 0;
187
+ },
188
+ removeItem: db.removeItem,
189
+ delayInit: true
190
+ };
191
+ }
192
+ return noopStorage;
193
+ }
194
+ const listsAtom = atomWithStorage(
195
+ "lists",
196
+ initialState,
197
+ IndexedDBStorage("lists")
198
+ );
199
+ function useListState() {
200
+ return useReducerAtom(listsAtom, reducer);
201
+ }
202
+ return {
203
+ listsAtom,
204
+ useListState
205
+ };
206
+ };
207
+
208
+ // react/useFetchListCallback.ts
209
+ import { nanoid } from "@reduxjs/toolkit";
210
+ import { useCallback } from "react";
211
+ function useFetchListCallback(dispatch) {
212
+ return useCallback(
213
+ async (listUrl, sendDispatch = true) => {
214
+ const requestId = nanoid();
215
+ if (sendDispatch) {
216
+ dispatch(fetchTokenList.pending({ requestId, url: listUrl }));
217
+ }
218
+ const getTokenList = (await import("./getTokenList-T45DK2AY.mjs")).default;
219
+ return getTokenList(listUrl).then((tokenList) => {
220
+ if (sendDispatch) {
221
+ dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId }));
222
+ }
223
+ return tokenList;
224
+ }).catch((error) => {
225
+ console.error(`Failed to get list at url ${listUrl}`, error);
226
+ if (sendDispatch) {
227
+ dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message }));
228
+ }
229
+ throw error;
230
+ });
231
+ },
232
+ [dispatch]
233
+ );
234
+ }
235
+ var useFetchListCallback_default = useFetchListCallback;
236
+ export {
237
+ NEW_LIST_STATE,
238
+ acceptListUpdate,
239
+ addList,
240
+ createListsAtom,
241
+ createTokenListReducer,
242
+ disableList,
243
+ enableList,
244
+ fetchTokenList,
245
+ rejectVersionUpdate,
246
+ removeList,
247
+ updateListVersion,
248
+ useFetchListCallback_default as useFetchListCallback
249
+ };
@@ -0,0 +1,39 @@
1
+ declare type ExtensionValue = string | number | boolean | null | undefined;
2
+ interface TokenInfo {
3
+ readonly chainId: number;
4
+ readonly address: string;
5
+ readonly name: string;
6
+ readonly decimals: number;
7
+ readonly symbol: string;
8
+ readonly logoURI?: string;
9
+ readonly tags?: string[];
10
+ readonly extensions?: {
11
+ readonly [key: string]: {
12
+ [key: string]: {
13
+ [key: string]: ExtensionValue;
14
+ } | ExtensionValue;
15
+ } | ExtensionValue;
16
+ };
17
+ }
18
+ interface Version {
19
+ readonly major: number;
20
+ readonly minor: number;
21
+ readonly patch: number;
22
+ }
23
+ interface Tags {
24
+ readonly [tagId: string]: {
25
+ readonly name: string;
26
+ readonly description: string;
27
+ };
28
+ }
29
+ interface TokenList {
30
+ readonly name: string;
31
+ readonly timestamp: string;
32
+ readonly version: Version;
33
+ readonly tokens: TokenInfo[];
34
+ readonly keywords?: string[];
35
+ readonly tags?: Tags;
36
+ readonly logoURI?: string;
37
+ }
38
+
39
+ export { TokenInfo as T, Version as V, TokenList as a, Tags as b };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@pancakeswap/token-lists",
3
+ "version": "0.0.2",
4
+ "sideEffects": false,
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsup",
10
+ "dev": "tsup --watch",
11
+ "test": "vitest --run",
12
+ "prepublishOnly": "yarn run build",
13
+ "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
14
+ },
15
+ "dependencies": {
16
+ "ajv": "^6.12.3",
17
+ "@pancakeswap/swap-sdk-core": "*"
18
+ },
19
+ "peerDependencies": {
20
+ "@reduxjs/toolkit": "^1.8.6",
21
+ "localforage": "^1.10.0",
22
+ "jotai": "^1.8.6",
23
+ "react": "^18.2.0"
24
+ },
25
+ "devDependencies": {
26
+ "@reduxjs/toolkit": "^1.8.6",
27
+ "react": "^18.2.0"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.mjs",
33
+ "require": "./dist/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react.d.ts",
37
+ "import": "./dist/react.mjs",
38
+ "require": "./dist/react.js"
39
+ }
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "react": {
43
+ "optional": true
44
+ },
45
+ "@reduxjs/toolkit": {
46
+ "optional": true
47
+ },
48
+ "localforage": {
49
+ "optional": true
50
+ },
51
+ "jotai": {
52
+ "optional": true
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,25 @@
1
+ import { ActionCreatorWithPayload, createAction } from '@reduxjs/toolkit'
2
+ import { TokenList, Version } from '../src/types'
3
+
4
+ export const fetchTokenList: Readonly<{
5
+ pending: ActionCreatorWithPayload<{ url: string; requestId: string }>
6
+ fulfilled: ActionCreatorWithPayload<{ url: string; tokenList: TokenList; requestId: string }>
7
+ rejected: ActionCreatorWithPayload<{ url: string; errorMessage: string; requestId: string }>
8
+ }> = {
9
+ pending: createAction('lists/fetchTokenList/pending'),
10
+ fulfilled: createAction('lists/fetchTokenList/fulfilled'),
11
+ rejected: createAction('lists/fetchTokenList/rejected'),
12
+ }
13
+ // add and remove from list options
14
+ export const addList = createAction<string>('lists/addList')
15
+ export const removeList = createAction<string>('lists/removeList')
16
+
17
+ // select which lists to search across from loaded lists
18
+ export const enableList = createAction<string>('lists/enableList')
19
+ export const disableList = createAction<string>('lists/disableList')
20
+
21
+ // versioning
22
+ export const acceptListUpdate = createAction<string>('lists/acceptListUpdate')
23
+ export const rejectVersionUpdate = createAction<Version>('lists/rejectVersionUpdate')
24
+
25
+ export const updateListVersion = createAction('lists/updateListVersion')
@@ -0,0 +1,46 @@
1
+ /* eslint-disable no-continue */
2
+ /* eslint-disable no-await-in-loop */
3
+ import { TokenList } from '@pancakeswap/token-lists'
4
+ import uriToHttp from '@pancakeswap/utils/uriToHttp'
5
+ import Ajv from 'ajv'
6
+ import schema from '../schema/pancakeswap.json'
7
+
8
+ export const tokenListValidator = new Ajv({ allErrors: true }).compile(schema)
9
+
10
+ /**
11
+ * Contains the logic for resolving a list URL to a validated token list
12
+ * @param listUrl list url
13
+ */
14
+ export default async function getTokenList(listUrl: string): Promise<TokenList> {
15
+ const urls: string[] = uriToHttp(listUrl)
16
+
17
+ for (let i = 0; i < urls.length; i++) {
18
+ const url = urls[i]
19
+ const isLast = i === urls.length - 1
20
+ let response
21
+ try {
22
+ response = await fetch(url)
23
+ } catch (error) {
24
+ console.error('Failed to fetch list', listUrl, error)
25
+ if (isLast) throw new Error(`Failed to download list ${listUrl}`)
26
+ continue
27
+ }
28
+
29
+ if (!response.ok) {
30
+ if (isLast) throw new Error(`Failed to download list ${listUrl}`)
31
+ continue
32
+ }
33
+
34
+ const json = await response.json()
35
+ if (!tokenListValidator(json)) {
36
+ const validationErrors: string =
37
+ tokenListValidator.errors?.reduce<string>((memo, error) => {
38
+ const add = `${(error as any).dataPath} ${error.message ?? ''}`
39
+ return memo.length > 0 ? `${memo}; ${add}` : `${add}`
40
+ }, '') ?? 'unknown error'
41
+ throw new Error(`Token list failed validation: ${validationErrors}`)
42
+ }
43
+ return json as TokenList
44
+ }
45
+ throw new Error('Unrecognized list URL protocol.')
46
+ }
package/react/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './reducer'
2
+ export * from './actions'
3
+ export * from './lists'
4
+ export { default as useFetchListCallback } from './useFetchListCallback'
package/react/lists.ts ADDED
@@ -0,0 +1,65 @@
1
+ import { atomWithStorage, useReducerAtom } from 'jotai/utils'
2
+ import localForage from 'localforage'
3
+ import { ListsState } from './reducer'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
6
+ function noop() {}
7
+ const noopStorage = {
8
+ getItem: noop,
9
+ setItem: noop,
10
+ removeItem: noop,
11
+ keys: [],
12
+ getAllKeys: noop,
13
+ }
14
+
15
+ export const createListsAtom = (storeName: string, reducer: any, initialState: any) => {
16
+ let gotOnce = false
17
+ /**
18
+ * Persist you redux state using IndexedDB
19
+ * @param {string} dbName - IndexedDB database name
20
+ */
21
+ function IndexedDBStorage(dbName: string) {
22
+ if (typeof window !== 'undefined') {
23
+ const db = localForage.createInstance({
24
+ name: dbName,
25
+ storeName,
26
+ })
27
+ return {
28
+ db,
29
+ getItem: async (key: string) => {
30
+ const value = await db.getItem(key)
31
+ gotOnce = true
32
+ if (value) {
33
+ return value
34
+ }
35
+ return initialState
36
+ },
37
+ setItem: (k: string, v: any) => {
38
+ if (gotOnce) {
39
+ return db.setItem(k, v)
40
+ }
41
+ return undefined
42
+ },
43
+ removeItem: db.removeItem,
44
+ delayInit: true,
45
+ }
46
+ }
47
+ return noopStorage
48
+ }
49
+
50
+ const listsAtom = atomWithStorage<ListsState>(
51
+ 'lists',
52
+ initialState,
53
+ // @ts-ignore
54
+ IndexedDBStorage('lists'),
55
+ )
56
+
57
+ function useListState() {
58
+ return useReducerAtom(listsAtom, reducer)
59
+ }
60
+
61
+ return {
62
+ listsAtom,
63
+ useListState,
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "main": "../dist/react.js",
3
+ "module": "../dist/react.mjs",
4
+ "types": "../dist/react.d.ts"
5
+ }