@exodus/assets-feature 7.2.2 → 7.4.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/CHANGELOG.md +12 -0
- package/module/assets-module.js +74 -19
- package/package.json +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [7.4.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@7.3.0...@exodus/assets-feature@7.4.0) (2025-04-17)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- feat: validate tokens using asset-schema-validation (#12011)
|
|
11
|
+
|
|
12
|
+
## [7.3.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@7.2.2...@exodus/assets-feature@7.3.0) (2025-04-16)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- feat: support updating combined asset members (#12019)
|
|
17
|
+
|
|
6
18
|
## [7.2.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@7.2.1...@exodus/assets-feature@7.2.2) (2025-04-14)
|
|
7
19
|
|
|
8
20
|
### Bug Fixes
|
package/module/assets-module.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @exodus/mutable/no-param-reassign-prop-only */
|
|
1
2
|
import {
|
|
2
3
|
createAssetRegistry,
|
|
3
4
|
CT_DEFAULT_SERVER,
|
|
4
5
|
CT_STATUS as STATUS,
|
|
5
6
|
CT_UPDATEABLE_PROPERTIES,
|
|
6
7
|
} from '@exodus/assets'
|
|
7
|
-
import { keyBy, mapValues, partition, pick, pickBy } from '@exodus/basic-utils'
|
|
8
|
+
import { keyBy, mapValues, partition, pick, pickBy, difference } from '@exodus/basic-utils'
|
|
8
9
|
import lodash from 'lodash'
|
|
9
10
|
import assert from 'minimalistic-assert'
|
|
10
11
|
import { memoizeLruCache } from '@exodus/asset-lib'
|
|
@@ -16,8 +17,9 @@ import {
|
|
|
16
17
|
} from './constants.js'
|
|
17
18
|
import { getAssetFromAssetId, getFetchErrorMessage, isDisabledCustomToken } from './utils.js'
|
|
18
19
|
import createFetchival from '@exodus/fetch/create-fetchival'
|
|
20
|
+
import { validateCustomToken, isValidCustomToken } from '@exodus/asset-schema-validation'
|
|
19
21
|
|
|
20
|
-
const { get, isEmpty, once } = lodash
|
|
22
|
+
const { get, isEmpty, once, uniq } = lodash
|
|
21
23
|
|
|
22
24
|
const FILTERED_FIELDS = [
|
|
23
25
|
'assetId',
|
|
@@ -77,7 +79,7 @@ export class AssetsModule {
|
|
|
77
79
|
#customTokensServerUrl
|
|
78
80
|
#updateableProps
|
|
79
81
|
#iconsStorage
|
|
80
|
-
#
|
|
82
|
+
#shouldValidateCustomToken
|
|
81
83
|
#assetPlugins
|
|
82
84
|
#combinedAssetsList
|
|
83
85
|
#storageDataKey
|
|
@@ -99,7 +101,6 @@ export class AssetsModule {
|
|
|
99
101
|
assetPlugins,
|
|
100
102
|
assetsAtom,
|
|
101
103
|
combinedAssetsList = [],
|
|
102
|
-
validateCustomToken = () => true,
|
|
103
104
|
config = {},
|
|
104
105
|
fetch,
|
|
105
106
|
logger,
|
|
@@ -114,7 +115,7 @@ export class AssetsModule {
|
|
|
114
115
|
this.#customTokensServerUrl = config.customTokensServerUrl || CT_DEFAULT_SERVER
|
|
115
116
|
this.#updateableProps = [...CT_UPDATEABLE_PROPERTIES, 'version']
|
|
116
117
|
this.#iconsStorage = iconsStorage
|
|
117
|
-
this.#
|
|
118
|
+
this.#shouldValidateCustomToken = config.shouldValidateCustomToken ?? true
|
|
118
119
|
this.#storageDataKey = config.storageDataKey || CT_DATA_KEY
|
|
119
120
|
this.#storageTimestampKey = config.storageTimestampKey || CT_TIMESTAMP_KEY
|
|
120
121
|
this.#customTokenUpdateInterval = config.customTokenUpdateInterval || CT_UPDATE_INTERVAL
|
|
@@ -222,11 +223,17 @@ export class AssetsModule {
|
|
|
222
223
|
}
|
|
223
224
|
|
|
224
225
|
const _token = await fetchTokenAndCacheError()
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
if (this.#shouldValidateCustomToken) {
|
|
227
|
+
try {
|
|
228
|
+
validateCustomToken(_token)
|
|
229
|
+
} catch (e) {
|
|
230
|
+
this.#logger.warn(
|
|
231
|
+
`Token did not pass validation ${baseAssetName} ${assetId}. Error: ${e.message}`
|
|
232
|
+
)
|
|
233
|
+
const err = new Error('Token did not pass validation')
|
|
234
|
+
this.#setCache(key, { cachedError: err })
|
|
235
|
+
throw err
|
|
236
|
+
}
|
|
230
237
|
}
|
|
231
238
|
|
|
232
239
|
const token = normalizeToken(_token)
|
|
@@ -247,13 +254,14 @@ export class AssetsModule {
|
|
|
247
254
|
|
|
248
255
|
try {
|
|
249
256
|
const token = await this.fetchToken(assetId, baseAssetName)
|
|
250
|
-
const { asset, isAdded } = this.#handleFetchedToken(token)
|
|
257
|
+
const { asset, isAdded, updates } = this.#handleFetchedToken(token)
|
|
258
|
+
const updated = updates.map(this.getAsset)
|
|
251
259
|
|
|
252
260
|
if (isAdded) {
|
|
253
261
|
await this.#storeCustomTokens([token])
|
|
254
|
-
this.#flushChanges({ added: [asset] })
|
|
262
|
+
this.#flushChanges({ added: [asset], updated })
|
|
255
263
|
} else {
|
|
256
|
-
this.#flushChanges({ updated: [asset] })
|
|
264
|
+
this.#flushChanges({ updated: [asset, ...updated] })
|
|
257
265
|
}
|
|
258
266
|
|
|
259
267
|
return asset
|
|
@@ -317,7 +325,7 @@ export class AssetsModule {
|
|
|
317
325
|
? await this.#fetch('tokens', { tokenNames: tokenNamesToFetch }, 'tokens')
|
|
318
326
|
: []
|
|
319
327
|
|
|
320
|
-
const validTokens = fetchedTokens.filter(this.#
|
|
328
|
+
const validTokens = fetchedTokens.filter(this.#isValidCustomToken).map(normalizeToken)
|
|
321
329
|
if (validTokens.length !== fetchedTokens.length)
|
|
322
330
|
this.#logger.warn('Invalid Custom Token schema')
|
|
323
331
|
|
|
@@ -365,6 +373,8 @@ export class AssetsModule {
|
|
|
365
373
|
(token) => _isDisabledCustomToken(token) && !isDisabledCustomToken(this.getAsset(token.name))
|
|
366
374
|
)
|
|
367
375
|
const updatedAssets = tokens.map(this.#registry.updateCustomToken)
|
|
376
|
+
const parentNames = uniq(tokens.flatMap(this.#handleCombinedParents))
|
|
377
|
+
updatedAssets.push(...parentNames.map(this.getAsset))
|
|
368
378
|
|
|
369
379
|
this.#flushChanges({ updated: updatedAssets, disabled })
|
|
370
380
|
}
|
|
@@ -398,7 +408,7 @@ export class AssetsModule {
|
|
|
398
408
|
{ baseAssetName: baseAssetNames, lifecycleStatus, query, excludeTags, pageNumber, pageSize },
|
|
399
409
|
'tokens'
|
|
400
410
|
)
|
|
401
|
-
const validTokens = tokens.filter(this.#
|
|
411
|
+
const validTokens = tokens.filter(this.#isValidCustomToken)
|
|
402
412
|
|
|
403
413
|
if (validTokens.length !== tokens.length) this.#logger.warn('Invalid Custom Token schema')
|
|
404
414
|
|
|
@@ -415,7 +425,7 @@ export class AssetsModule {
|
|
|
415
425
|
|
|
416
426
|
#fetchUpdates = async (assetVersions) => {
|
|
417
427
|
const updatedTokens = await this.#fetch('updates', assetVersions, 'tokens')
|
|
418
|
-
const validatedTokens = updatedTokens.filter(this.#
|
|
428
|
+
const validatedTokens = updatedTokens.filter(this.#isValidCustomToken).map(normalizeToken)
|
|
419
429
|
if (validatedTokens.length !== updatedTokens.length) {
|
|
420
430
|
this.#logger.warn('Invalid Custom Token schema')
|
|
421
431
|
}
|
|
@@ -468,33 +478,70 @@ export class AssetsModule {
|
|
|
468
478
|
}
|
|
469
479
|
}
|
|
470
480
|
|
|
481
|
+
#handleCombinedParents = ({ name, parents }) => {
|
|
482
|
+
if (!parents) return []
|
|
483
|
+
assert(Array.isArray(parents), 'Array expected')
|
|
484
|
+
|
|
485
|
+
const currentParents = this.#getAssetNamesBy(
|
|
486
|
+
(asset) => asset.isCombined && asset.combinedAssetNames.includes(name)
|
|
487
|
+
)
|
|
488
|
+
const updatedCombinedAssetNames = []
|
|
489
|
+
|
|
490
|
+
const removedParents = difference(currentParents, parents)
|
|
491
|
+
removedParents.forEach((parent) => {
|
|
492
|
+
this.#registry.updateCombinedAsset(parent, { removeMembers: [name] })
|
|
493
|
+
updatedCombinedAssetNames.push(parent)
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
const addedParents = difference(parents, currentParents)
|
|
497
|
+
addedParents.forEach((parent) => {
|
|
498
|
+
const asset = this.getAsset(parent)
|
|
499
|
+
if (!asset || !asset.isCombined) return
|
|
500
|
+
|
|
501
|
+
this.#registry.updateCombinedAsset(parent, { addMembers: [name] })
|
|
502
|
+
updatedCombinedAssetNames.push(parent)
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
return uniq(updatedCombinedAssetNames)
|
|
506
|
+
}
|
|
507
|
+
|
|
471
508
|
#handleFetchedToken = (token) => {
|
|
472
509
|
const asset = this.getAsset(token.name)
|
|
473
|
-
if (asset) return { asset, isAdded: false }
|
|
510
|
+
if (asset) return { asset, isAdded: false, updates: [] }
|
|
474
511
|
|
|
475
512
|
const { name } = this.#registry.addCustomToken(token) // add to registry
|
|
513
|
+
const updates = this.#handleCombinedParents(token)
|
|
514
|
+
|
|
476
515
|
this.#logger.log('Custom token added:', name)
|
|
477
|
-
return { asset: this.getAsset(name), isAdded: true }
|
|
516
|
+
return { asset: this.getAsset(name), isAdded: true, updates }
|
|
478
517
|
}
|
|
479
518
|
|
|
480
519
|
#handleFetchedTokens = (tokens) => {
|
|
481
520
|
const fetchedAndHandledTokens = []
|
|
482
521
|
const added = []
|
|
483
522
|
const updated = []
|
|
523
|
+
const parentNames = []
|
|
484
524
|
for (const token of tokens) {
|
|
485
525
|
try {
|
|
486
|
-
const { asset, isAdded } = this.#handleFetchedToken(token)
|
|
526
|
+
const { asset, isAdded, updates } = this.#handleFetchedToken(token)
|
|
527
|
+
|
|
487
528
|
if (isAdded) {
|
|
488
529
|
added.push(asset)
|
|
489
530
|
fetchedAndHandledTokens.push(token)
|
|
490
531
|
} else {
|
|
491
532
|
updated.push(asset)
|
|
492
533
|
}
|
|
534
|
+
|
|
535
|
+
if (updates.length > 0) {
|
|
536
|
+
parentNames.push(...updates)
|
|
537
|
+
}
|
|
493
538
|
} catch (err) {
|
|
494
539
|
this.#logger.warn('Handle fetched custom tokens error:', err.message)
|
|
495
540
|
}
|
|
496
541
|
}
|
|
497
542
|
|
|
543
|
+
updated.push(...uniq(parentNames).map(this.getAsset))
|
|
544
|
+
|
|
498
545
|
return { fetchedAndHandledTokens, added, updated }
|
|
499
546
|
}
|
|
500
547
|
|
|
@@ -585,6 +632,14 @@ export class AssetsModule {
|
|
|
585
632
|
return !lastUpdateDate || Date.now() - lastUpdateDate > this.#customTokenUpdateInterval
|
|
586
633
|
}
|
|
587
634
|
|
|
635
|
+
#isValidCustomToken = (token) => {
|
|
636
|
+
if (!this.#shouldValidateCustomToken) {
|
|
637
|
+
return true
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return isValidCustomToken(token)
|
|
641
|
+
}
|
|
642
|
+
|
|
588
643
|
clear = async () =>
|
|
589
644
|
this.#storage &&
|
|
590
645
|
Promise.all([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/assets-feature",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "This Exodus SDK feature provides access to instances of all blockchain asset adapters supported by the wallet, and enables you to search for and add custom tokens at runtime.",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@exodus/asset-lib": "^5.3.0",
|
|
39
|
-
"@exodus/
|
|
39
|
+
"@exodus/asset-schema-validation": "^1.0.1",
|
|
40
|
+
"@exodus/assets": "^11.3.0",
|
|
40
41
|
"@exodus/atoms": "^9.0.0",
|
|
41
42
|
"@exodus/basic-utils": "^4.0.0",
|
|
42
43
|
"@exodus/fetch": "^1.3.0",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"@exodus/cosmos-plugin": "^1.3.3",
|
|
61
62
|
"@exodus/ethereum-lib": "^5.0.0",
|
|
62
63
|
"@exodus/ethereum-meta": "^2.0.0",
|
|
64
|
+
"@exodus/ethereum-plugin": "^2.7.4",
|
|
63
65
|
"@exodus/fusion-local": "^2.1.0",
|
|
64
66
|
"@exodus/keychain": "^7.3.0",
|
|
65
67
|
"@exodus/logger": "^1.2.3",
|
|
@@ -68,7 +70,7 @@
|
|
|
68
70
|
"@exodus/public-key-provider": "^4.1.1",
|
|
69
71
|
"@exodus/redux-dependency-injection": "^4.1.1",
|
|
70
72
|
"@exodus/storage-memory": "^2.2.2",
|
|
71
|
-
"@exodus/wallet-accounts": "^17.5.
|
|
73
|
+
"@exodus/wallet-accounts": "^17.5.1",
|
|
72
74
|
"@exodus/wild-emitter": "^1.0.0",
|
|
73
75
|
"bip39": "^3.1.0",
|
|
74
76
|
"events": "^3.3.0",
|
|
@@ -78,5 +80,5 @@
|
|
|
78
80
|
"publishConfig": {
|
|
79
81
|
"access": "public"
|
|
80
82
|
},
|
|
81
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "05dfc1fd50441f5b6513eef369476167c3b02c57"
|
|
82
84
|
}
|