@pancakeswap/token-lists 0.0.16 → 0.1.0
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 +12 -12
- 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 +2 -2
- package/dist/index.js +63 -15
- package/dist/index.mjs +62 -1
- package/dist/react.d.ts +32 -9
- package/dist/react.js +223 -88
- package/dist/react.mjs +212 -81
- package/dist/{types-d6730d17.d.ts → types-92a8b029.d.ts} +1 -0
- package/package.json +4 -3
- package/react/actions.ts +11 -0
- package/react/getTokenList.ts +2 -1
- package/react/index.test.ts +3 -0
- package/react/lists.ts +145 -27
- package/react/reducer.ts +34 -53
- package/react/reducerHelpers.ts +111 -0
- package/schema/pancakeswap.json +9 -9
- package/src/types.ts +1 -0
package/react/lists.ts
CHANGED
|
@@ -3,10 +3,15 @@ import { atom, useAtom, useAtomValue } from 'jotai'
|
|
|
3
3
|
import { atomFamily, atomWithStorage, loadable } from 'jotai/utils'
|
|
4
4
|
import { type AsyncStorage } from 'jotai/vanilla/utils/atomWithStorage'
|
|
5
5
|
import localForage from 'localforage'
|
|
6
|
-
import
|
|
6
|
+
import debounce from 'lodash/debounce'
|
|
7
|
+
import {
|
|
8
|
+
fetchTokenList,
|
|
9
|
+
batchFetchTokenListPending,
|
|
10
|
+
batchFetchTokenListFulfilled,
|
|
11
|
+
batchFetchTokenListRejected,
|
|
12
|
+
} from './actions'
|
|
7
13
|
import { getTokenList } from './getTokenList'
|
|
8
14
|
import { ListsState } from './reducer'
|
|
9
|
-
|
|
10
15
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
11
16
|
function noop() {}
|
|
12
17
|
|
|
@@ -41,7 +46,7 @@ export function findTokenBySymbol(state: ListsState, chainId: number, symbol: st
|
|
|
41
46
|
|
|
42
47
|
export const createListsAtom = (storeName: string, reducer: any, initialState: any) => {
|
|
43
48
|
/**
|
|
44
|
-
* Persist
|
|
49
|
+
* Persist only token lists using IndexedDB - optimized storage format
|
|
45
50
|
* @param {string} dbName - IndexedDB database name
|
|
46
51
|
*/
|
|
47
52
|
function IndexedDBStorage<Value>(dbName: string): AsyncStorage<Value> {
|
|
@@ -50,17 +55,27 @@ export const createListsAtom = (storeName: string, reducer: any, initialState: a
|
|
|
50
55
|
name: dbName,
|
|
51
56
|
storeName,
|
|
52
57
|
})
|
|
58
|
+
const mem = new Map<string, any>()
|
|
59
|
+
|
|
60
|
+
const debouncedSetItem = debounce(async (k: string, v: any) => {
|
|
61
|
+
db.setItem(k, v)
|
|
62
|
+
}, 300)
|
|
63
|
+
|
|
53
64
|
return {
|
|
54
65
|
getItem: async (key: string) => {
|
|
66
|
+
if (mem.has(key)) {
|
|
67
|
+
return mem.get(key)
|
|
68
|
+
}
|
|
55
69
|
const value = await db.getItem(key)
|
|
56
70
|
if (value) {
|
|
57
71
|
return value
|
|
58
72
|
}
|
|
59
|
-
return
|
|
73
|
+
return undefined as any
|
|
60
74
|
},
|
|
61
75
|
setItem: async (k: string, v: any) => {
|
|
62
76
|
if (v === EMPTY) return
|
|
63
|
-
|
|
77
|
+
mem.set(k, v)
|
|
78
|
+
debouncedSetItem(k, v)
|
|
64
79
|
},
|
|
65
80
|
removeItem: db.removeItem,
|
|
66
81
|
}
|
|
@@ -68,63 +83,166 @@ export const createListsAtom = (storeName: string, reducer: any, initialState: a
|
|
|
68
83
|
return noopStorage
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
|
|
86
|
+
// Storage for token lists only - optimized format: { [url]: TokenList }
|
|
87
|
+
const tokenListsStorageAtom = atomWithStorage<Record<string, any> | typeof EMPTY>(
|
|
88
|
+
'tokenLists',
|
|
89
|
+
EMPTY,
|
|
90
|
+
IndexedDBStorage('tokenLists'),
|
|
91
|
+
)
|
|
72
92
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
// Memory state atom that holds the full ListsState
|
|
94
|
+
const memoryStateAtom = atom<ListsState>(initialState as ListsState)
|
|
95
|
+
|
|
96
|
+
const listStateAtom = atom<ListsState>((get) => {
|
|
97
|
+
// Separate this(mem/storage) is a prepare for refactor of the list part
|
|
98
|
+
const memoryState = get(memoryStateAtom)
|
|
99
|
+
const value = get(loadable(tokenListsStorageAtom))
|
|
100
|
+
|
|
101
|
+
if (value.state === 'hasData' && value.data && value.data !== EMPTY) {
|
|
102
|
+
const storedTokenLists = value.data as Record<string, any>
|
|
103
|
+
const reconstructedState = { ...memoryState }
|
|
104
|
+
|
|
105
|
+
const updatedByUrl = { ...reconstructedState.byUrl }
|
|
106
|
+
Object.keys(storedTokenLists).forEach((url) => {
|
|
107
|
+
if (storedTokenLists[url]) {
|
|
108
|
+
updatedByUrl[url] = {
|
|
109
|
+
...updatedByUrl[url],
|
|
110
|
+
current: storedTokenLists[url],
|
|
111
|
+
// Keep existing memory state for loading, error, pendingUpdate
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
reconstructedState.byUrl = updatedByUrl
|
|
116
|
+
|
|
117
|
+
return reconstructedState
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return memoryState
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const updateListStateAtom = atom<null, any, void>(null, async (get, set, action) => {
|
|
124
|
+
const currentMemoryState = get(memoryStateAtom)
|
|
125
|
+
const newState = reducer(currentMemoryState, action)
|
|
126
|
+
|
|
127
|
+
// Update memory state
|
|
128
|
+
set(memoryStateAtom, { ...newState })
|
|
129
|
+
|
|
130
|
+
// Extract only current token lists for storage
|
|
131
|
+
const tokenListsToStore: Record<string, any> = {}
|
|
132
|
+
Object.keys(newState.byUrl).forEach((url) => {
|
|
133
|
+
if (newState.byUrl[url]?.current) {
|
|
134
|
+
tokenListsToStore[url] = newState.byUrl[url].current
|
|
78
135
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
// Store only the token lists, not the full state
|
|
139
|
+
set(tokenListsStorageAtom, tokenListsToStore)
|
|
140
|
+
})
|
|
85
141
|
|
|
86
|
-
const isReadyAtom = loadable(
|
|
142
|
+
const isReadyAtom = loadable(tokenListsStorageAtom)
|
|
87
143
|
|
|
88
144
|
const tokenAtom = atomFamily((key: { chainId: number; address: string }) =>
|
|
89
|
-
atom((get) => findTokenByAddress(get(
|
|
145
|
+
atom((get) => findTokenByAddress(get(listStateAtom), key.chainId, key.address)),
|
|
90
146
|
)
|
|
91
147
|
|
|
92
148
|
const tokenBySymbolAtom = atomFamily((key: { chainId: number; symbol: string }) =>
|
|
93
|
-
atom((get) => findTokenBySymbol(get(
|
|
149
|
+
atom((get) => findTokenBySymbol(get(listStateAtom), key.chainId, key.symbol)),
|
|
94
150
|
)
|
|
95
151
|
|
|
96
152
|
const fetchListAtom = atom<null, [string], Promise<void>>(null, async (get, set, url) => {
|
|
97
|
-
const state = get(
|
|
153
|
+
const state = get(listStateAtom)
|
|
98
154
|
const listState = state.byUrl[url]
|
|
99
155
|
if (listState?.current || listState?.loadingRequestId) {
|
|
100
156
|
return
|
|
101
157
|
}
|
|
102
158
|
|
|
103
159
|
const requestId = nanoid()
|
|
104
|
-
set(
|
|
160
|
+
set(updateListStateAtom, fetchTokenList.pending({ url, requestId }))
|
|
105
161
|
|
|
106
162
|
try {
|
|
107
163
|
const tokenList = await getTokenList(url)
|
|
108
|
-
set(
|
|
164
|
+
set(updateListStateAtom, fetchTokenList.fulfilled({ url, tokenList: tokenList!, requestId }))
|
|
165
|
+
} catch (error: any) {
|
|
166
|
+
set(updateListStateAtom, fetchTokenList.rejected({ url, requestId, errorMessage: error.message }))
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const fetchListBatchAtom = atom<null, [string[]], Promise<void>>(null, async (get, set, urls) => {
|
|
171
|
+
const state = get(listStateAtom)
|
|
172
|
+
|
|
173
|
+
// Filter out URLs that are already loaded or loading
|
|
174
|
+
const urlsToFetch = urls.filter((url) => {
|
|
175
|
+
const listState = state.byUrl[url]
|
|
176
|
+
return !listState?.current && !listState?.loadingRequestId
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
if (urlsToFetch.length === 0) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const requestId = nanoid()
|
|
184
|
+
set(updateListStateAtom, batchFetchTokenListPending({ urls: urlsToFetch, requestId }))
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
// Fetch all token lists in parallel
|
|
188
|
+
const results = await Promise.allSettled(
|
|
189
|
+
urlsToFetch.map(async (url) => {
|
|
190
|
+
const tokenList = await getTokenList(url)
|
|
191
|
+
return { url, tokenList: tokenList!, requestId }
|
|
192
|
+
}),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Separate successful and failed results
|
|
196
|
+
const fulfilled: Array<{ url: string; tokenList: any; requestId: string }> = []
|
|
197
|
+
const rejected: Array<{ url: string; errorMessage: string; requestId: string }> = []
|
|
198
|
+
|
|
199
|
+
results.forEach((result, index) => {
|
|
200
|
+
const url = urlsToFetch[index]
|
|
201
|
+
if (result.status === 'fulfilled') {
|
|
202
|
+
fulfilled.push(result.value)
|
|
203
|
+
} else {
|
|
204
|
+
rejected.push({
|
|
205
|
+
url,
|
|
206
|
+
errorMessage: result.reason?.message || 'Unknown error',
|
|
207
|
+
requestId,
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// Dispatch batch actions
|
|
213
|
+
if (fulfilled.length > 0) {
|
|
214
|
+
set(updateListStateAtom, batchFetchTokenListFulfilled({ results: fulfilled }))
|
|
215
|
+
}
|
|
216
|
+
if (rejected.length > 0) {
|
|
217
|
+
set(updateListStateAtom, batchFetchTokenListRejected({ errors: rejected }))
|
|
218
|
+
}
|
|
109
219
|
} catch (error: any) {
|
|
110
|
-
|
|
220
|
+
// Fallback to individual rejections if batch processing fails
|
|
221
|
+
const errors = urlsToFetch.map((url) => ({
|
|
222
|
+
url,
|
|
223
|
+
errorMessage: error.message,
|
|
224
|
+
requestId,
|
|
225
|
+
}))
|
|
226
|
+
set(updateListStateAtom, batchFetchTokenListRejected({ errors }))
|
|
111
227
|
}
|
|
112
228
|
})
|
|
113
229
|
|
|
114
230
|
function useListState() {
|
|
115
|
-
return useAtom(
|
|
231
|
+
return useAtom(listStateAtom)
|
|
116
232
|
}
|
|
117
233
|
|
|
118
234
|
function useListStateReady() {
|
|
119
235
|
const value = useAtomValue(isReadyAtom)
|
|
120
|
-
return value.state === 'hasData'
|
|
236
|
+
return value.state === 'hasData'
|
|
121
237
|
}
|
|
122
238
|
|
|
123
239
|
return {
|
|
124
|
-
listsAtom:
|
|
240
|
+
listsAtom: listStateAtom,
|
|
241
|
+
updateListStateAtom,
|
|
125
242
|
tokenAtom,
|
|
126
243
|
tokenBySymbolAtom,
|
|
127
244
|
fetchListAtom,
|
|
245
|
+
fetchListBatchAtom,
|
|
128
246
|
useListStateReady,
|
|
129
247
|
useListState,
|
|
130
248
|
}
|
package/react/reducer.ts
CHANGED
|
@@ -8,8 +8,12 @@ import {
|
|
|
8
8
|
enableList,
|
|
9
9
|
disableList,
|
|
10
10
|
updateListVersion,
|
|
11
|
+
batchFetchTokenListPending,
|
|
12
|
+
batchFetchTokenListFulfilled,
|
|
13
|
+
batchFetchTokenListRejected,
|
|
11
14
|
} from './actions'
|
|
12
|
-
import {
|
|
15
|
+
import { TokenList } from '../src/types'
|
|
16
|
+
import { setPendingTokenList, setFulfilledTokenList, setRejectedTokenList, batchActivateUrls } from './reducerHelpers'
|
|
13
17
|
|
|
14
18
|
export interface ListsState {
|
|
15
19
|
readonly byUrl: {
|
|
@@ -44,62 +48,13 @@ export const createTokenListReducer = (
|
|
|
44
48
|
createReducer(initialState, (builder) =>
|
|
45
49
|
builder
|
|
46
50
|
.addCase(fetchTokenList.pending, (state, { payload: { requestId, url } }) => {
|
|
47
|
-
|
|
48
|
-
const pendingUpdate = state.byUrl[url]?.pendingUpdate ?? null
|
|
49
|
-
|
|
50
|
-
state.byUrl[url] = {
|
|
51
|
-
current,
|
|
52
|
-
pendingUpdate,
|
|
53
|
-
loadingRequestId: requestId,
|
|
54
|
-
error: null,
|
|
55
|
-
}
|
|
51
|
+
setPendingTokenList(state, url, requestId)
|
|
56
52
|
})
|
|
57
53
|
.addCase(fetchTokenList.fulfilled, (state, { payload: { requestId, tokenList, url } }) => {
|
|
58
|
-
|
|
59
|
-
const loadingRequestId = state.byUrl[url]?.loadingRequestId
|
|
60
|
-
|
|
61
|
-
// no-op if update does nothing
|
|
62
|
-
if (current) {
|
|
63
|
-
const upgradeType = getVersionUpgrade(current.version, tokenList.version)
|
|
64
|
-
|
|
65
|
-
if (upgradeType === VersionUpgrade.NONE) return
|
|
66
|
-
if (loadingRequestId === null || loadingRequestId === requestId) {
|
|
67
|
-
state.byUrl[url] = {
|
|
68
|
-
...state.byUrl[url],
|
|
69
|
-
loadingRequestId: null,
|
|
70
|
-
error: null,
|
|
71
|
-
current,
|
|
72
|
-
pendingUpdate: tokenList,
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
// activate if on default active
|
|
77
|
-
if (DEFAULT_ACTIVE_LIST_URLS.includes(url) && state.activeListUrls && !state.activeListUrls.includes(url)) {
|
|
78
|
-
state.activeListUrls.push(url)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
state.byUrl[url] = {
|
|
82
|
-
...state.byUrl[url],
|
|
83
|
-
loadingRequestId: null,
|
|
84
|
-
error: null,
|
|
85
|
-
current: tokenList,
|
|
86
|
-
pendingUpdate: null,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
54
|
+
setFulfilledTokenList(state, url, tokenList, requestId, DEFAULT_ACTIVE_LIST_URLS)
|
|
89
55
|
})
|
|
90
56
|
.addCase(fetchTokenList.rejected, (state, { payload: { url, requestId, errorMessage } }) => {
|
|
91
|
-
|
|
92
|
-
// no-op since it's not the latest request
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
state.byUrl[url] = {
|
|
97
|
-
...state.byUrl[url],
|
|
98
|
-
loadingRequestId: null,
|
|
99
|
-
error: errorMessage,
|
|
100
|
-
current: null,
|
|
101
|
-
pendingUpdate: null,
|
|
102
|
-
}
|
|
57
|
+
setRejectedTokenList(state, url, requestId, errorMessage)
|
|
103
58
|
})
|
|
104
59
|
.addCase(addList, (state, { payload: url }) => {
|
|
105
60
|
if (!state.byUrl[url]) {
|
|
@@ -182,5 +137,31 @@ export const createTokenListReducer = (
|
|
|
182
137
|
return true
|
|
183
138
|
})
|
|
184
139
|
}
|
|
140
|
+
})
|
|
141
|
+
.addCase(batchFetchTokenListPending, (state, { payload: { urls, requestId } }) => {
|
|
142
|
+
// Batch update multiple lists to pending state in a single update cycle
|
|
143
|
+
urls.forEach((url) => {
|
|
144
|
+
setPendingTokenList(state, url, requestId)
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
.addCase(batchFetchTokenListFulfilled, (state, { payload: { results } }) => {
|
|
148
|
+
// Batch update multiple lists to fulfilled state in a single update cycle
|
|
149
|
+
const urlsToActivate: string[] = []
|
|
150
|
+
|
|
151
|
+
results.forEach(({ url, tokenList, requestId }) => {
|
|
152
|
+
const isNewList = setFulfilledTokenList(state, url, tokenList, requestId, DEFAULT_ACTIVE_LIST_URLS)
|
|
153
|
+
if (isNewList && DEFAULT_ACTIVE_LIST_URLS.includes(url)) {
|
|
154
|
+
urlsToActivate.push(url)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
// Batch activate URLs if needed
|
|
159
|
+
batchActivateUrls(state, urlsToActivate)
|
|
160
|
+
})
|
|
161
|
+
.addCase(batchFetchTokenListRejected, (state, { payload: { errors } }) => {
|
|
162
|
+
// Batch update multiple lists to rejected state in a single update cycle
|
|
163
|
+
errors.forEach(({ url, requestId, errorMessage }) => {
|
|
164
|
+
setRejectedTokenList(state, url, requestId, errorMessage)
|
|
165
|
+
})
|
|
185
166
|
}),
|
|
186
167
|
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { getVersionUpgrade, VersionUpgrade } from '../src/getVersionUpgrade'
|
|
2
|
+
import { TokenList } from '../src/types'
|
|
3
|
+
|
|
4
|
+
// Mutable state type for reducer operations
|
|
5
|
+
type MutableListsState = {
|
|
6
|
+
byUrl: {
|
|
7
|
+
[url: string]: {
|
|
8
|
+
current: TokenList | null
|
|
9
|
+
pendingUpdate: TokenList | null
|
|
10
|
+
loadingRequestId: string | null
|
|
11
|
+
error: string | null
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
lastInitializedDefaultListOfLists?: string[]
|
|
15
|
+
activeListUrls: string[] | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Core function to set a single list to pending state
|
|
20
|
+
*/
|
|
21
|
+
export const setPendingTokenList = (state: MutableListsState, url: string, requestId: string): void => {
|
|
22
|
+
const current = state.byUrl[url]?.current ?? null
|
|
23
|
+
const pendingUpdate = state.byUrl[url]?.pendingUpdate ?? null
|
|
24
|
+
|
|
25
|
+
state.byUrl[url] = {
|
|
26
|
+
current,
|
|
27
|
+
pendingUpdate,
|
|
28
|
+
loadingRequestId: requestId,
|
|
29
|
+
error: null,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Core function to set a single list to fulfilled state
|
|
35
|
+
*/
|
|
36
|
+
export const setFulfilledTokenList = (
|
|
37
|
+
state: MutableListsState,
|
|
38
|
+
url: string,
|
|
39
|
+
tokenList: TokenList,
|
|
40
|
+
requestId: string,
|
|
41
|
+
DEFAULT_ACTIVE_LIST_URLS: string[],
|
|
42
|
+
): boolean => {
|
|
43
|
+
const current = state.byUrl[url]?.current
|
|
44
|
+
const loadingRequestId = state.byUrl[url]?.loadingRequestId
|
|
45
|
+
|
|
46
|
+
// no-op if update does nothing
|
|
47
|
+
if (current) {
|
|
48
|
+
const upgradeType = getVersionUpgrade(current.version, tokenList.version)
|
|
49
|
+
|
|
50
|
+
if (upgradeType === VersionUpgrade.NONE) return false
|
|
51
|
+
if (loadingRequestId === null || loadingRequestId === requestId) {
|
|
52
|
+
state.byUrl[url] = {
|
|
53
|
+
...state.byUrl[url],
|
|
54
|
+
loadingRequestId: null,
|
|
55
|
+
error: null,
|
|
56
|
+
current,
|
|
57
|
+
pendingUpdate: tokenList,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false // not a new list, so no activation needed
|
|
61
|
+
}
|
|
62
|
+
// activate if on default active
|
|
63
|
+
if (DEFAULT_ACTIVE_LIST_URLS.includes(url) && state.activeListUrls && !state.activeListUrls.includes(url)) {
|
|
64
|
+
state.activeListUrls.push(url)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
state.byUrl[url] = {
|
|
68
|
+
...state.byUrl[url],
|
|
69
|
+
loadingRequestId: null,
|
|
70
|
+
error: null,
|
|
71
|
+
current: tokenList,
|
|
72
|
+
pendingUpdate: null,
|
|
73
|
+
}
|
|
74
|
+
return true // new list, might need activation
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Core function to set a single list to rejected state
|
|
79
|
+
*/
|
|
80
|
+
export const setRejectedTokenList = (
|
|
81
|
+
state: MutableListsState,
|
|
82
|
+
url: string,
|
|
83
|
+
requestId: string,
|
|
84
|
+
errorMessage: string,
|
|
85
|
+
): void => {
|
|
86
|
+
if (state.byUrl[url]?.loadingRequestId !== requestId) {
|
|
87
|
+
// no-op since it's not the latest request
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
state.byUrl[url] = {
|
|
92
|
+
...state.byUrl[url],
|
|
93
|
+
loadingRequestId: null,
|
|
94
|
+
error: errorMessage,
|
|
95
|
+
current: null,
|
|
96
|
+
pendingUpdate: null,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Batch activate URLs if needed
|
|
102
|
+
*/
|
|
103
|
+
export const batchActivateUrls = (state: MutableListsState, urls: string[]): void => {
|
|
104
|
+
if (urls.length > 0 && state.activeListUrls) {
|
|
105
|
+
urls.forEach((url) => {
|
|
106
|
+
if (!state.activeListUrls!.includes(url)) {
|
|
107
|
+
state.activeListUrls!.push(url)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
}
|
package/schema/pancakeswap.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
{
|
|
12
12
|
"major": 1,
|
|
13
13
|
"minor": 0,
|
|
14
|
-
"patch":
|
|
14
|
+
"patch": 1
|
|
15
15
|
}
|
|
16
16
|
],
|
|
17
17
|
"additionalProperties": false,
|
|
@@ -196,17 +196,17 @@
|
|
|
196
196
|
"type": "string",
|
|
197
197
|
"description": "The name of the token",
|
|
198
198
|
"minLength": 1,
|
|
199
|
-
"maxLength":
|
|
200
|
-
"pattern": "^[ \\w.'
|
|
201
|
-
"examples": ["USD Coin"]
|
|
199
|
+
"maxLength": 60,
|
|
200
|
+
"pattern": "^[ \\w.'+\\-%/,\\$À-ÖØ-öø-ÿ:&\\[\\]\\(\\)\\u4e00-\\u9fa5]+$",
|
|
201
|
+
"examples": ["USD Coin", "币安币"]
|
|
202
202
|
},
|
|
203
203
|
"symbol": {
|
|
204
204
|
"type": "string",
|
|
205
205
|
"description": "The symbol for the token; must be alphanumeric",
|
|
206
|
-
"pattern": "^[a-zA-Z0-9+\\-%/$.\\
|
|
206
|
+
"pattern": "^[a-zA-Z0-9+\\-%/$.\\s_\\u4e00-\\u9fa5]+$",
|
|
207
207
|
"minLength": 1,
|
|
208
208
|
"maxLength": 20,
|
|
209
|
-
"examples": ["USDC"]
|
|
209
|
+
"examples": ["USDC", "币安"]
|
|
210
210
|
},
|
|
211
211
|
"logoURI": {
|
|
212
212
|
"type": "string",
|
|
@@ -256,14 +256,14 @@
|
|
|
256
256
|
"type": "string",
|
|
257
257
|
"description": "The name of the token",
|
|
258
258
|
"minLength": 1,
|
|
259
|
-
"maxLength":
|
|
260
|
-
"pattern": "^[ \\w.'
|
|
259
|
+
"maxLength": 60,
|
|
260
|
+
"pattern": "^[ \\w.'+\\-%/,\\$À-ÖØ-öø-ÿ:&\\[\\]\\(\\)]+$",
|
|
261
261
|
"examples": ["USD Coin"]
|
|
262
262
|
},
|
|
263
263
|
"symbol": {
|
|
264
264
|
"type": "string",
|
|
265
265
|
"description": "The symbol for the token; must be alphanumeric",
|
|
266
|
-
"pattern": "^[a-zA-Z0-9+\\-%/$.]+$",
|
|
266
|
+
"pattern": "^[a-zA-Z0-9+\\-%/$._]+$",
|
|
267
267
|
"minLength": 1,
|
|
268
268
|
"maxLength": 20,
|
|
269
269
|
"examples": ["USDC"]
|