@exodus/assets-feature 7.3.0 → 7.4.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/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.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@7.4.0...@exodus/assets-feature@7.4.1) (2025-05-15)
7
+
8
+ ### Bug Fixes
9
+
10
+ - fix: stop assets-feature monitor on stop (#12460)
11
+
12
+ ## [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)
13
+
14
+ ### Features
15
+
16
+ - feat: validate tokens using asset-schema-validation (#12011)
17
+
6
18
  ## [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)
7
19
 
8
20
  ### Features
@@ -118,6 +118,11 @@ class AssetClientInterface {
118
118
  accountState,
119
119
  batch = this.#blockchainMetadata.batch(),
120
120
  }) => {
121
+ const asset = this.#assetsModule.getAsset(assetName)
122
+ if (!asset.baseAsset.api.features.accountState) {
123
+ return batch
124
+ }
125
+
121
126
  // merge mem to keep the previous accountMem behavior
122
127
  if (!isEmpty(newData?.mem) && (!accountState || accountState.mem)) {
123
128
  newData = { ...newData, mem: { ...accountState?.mem, ...newData.mem } }
@@ -166,12 +171,8 @@ class AssetClientInterface {
166
171
  assert(asset, `assetName ${assetName} is not supported`)
167
172
  assert(walletAccountInstance, `walletAccountInstance ${walletAccount} is not available`)
168
173
 
169
- const baseAsset = asset.baseAsset
170
- const out = {
171
- confirmationsNumber: baseAsset.api?.getConfirmationsNumber
172
- ? baseAsset.api.getConfirmationsNumber()
173
- : 1,
174
- }
174
+ const confirmationsNumber = await this.getConfirmationsNumber({ assetName })
175
+ const out = { confirmationsNumber }
175
176
 
176
177
  const gapLimit =
177
178
  this.#config?.compatibilityModeGapLimits?.[walletAccountInstance.compatibilityMode]
@@ -223,12 +224,17 @@ class AssetClientInterface {
223
224
  addressIndex = 0,
224
225
  chainIndex = 0,
225
226
  }) => {
227
+ const [walletAccountInstance, defaultPurpose] = await Promise.all([
228
+ this.#getWalletAccount(walletAccount),
229
+ purpose === undefined
230
+ ? this.#assetSources.getDefaultPurpose({ assetName, walletAccount })
231
+ : undefined,
232
+ ])
226
233
  if (purpose === undefined) {
227
- ;[purpose] = await this.getSupportedPurposes({ assetName, walletAccount })
234
+ purpose = defaultPurpose
228
235
  }
229
236
 
230
237
  const asset = this.#assetsModule.getAsset(assetName)
231
- const walletAccountInstance = await this.#getWalletAccount(walletAccount)
232
238
  const keyIdentifier = asset.baseAsset.api.getKeyIdentifier({
233
239
  purpose,
234
240
  accountIndex: walletAccountInstance.index,
@@ -244,18 +250,18 @@ class AssetClientInterface {
244
250
  }
245
251
 
246
252
  getExtendedPublicKey = async ({ assetName, walletAccount, purpose }) => {
247
- const asset = this.#assetsModule.getAsset(assetName)
248
- const [walletAccountInstance, purposes] = await Promise.all([
253
+ const [walletAccountInstance, defaultPurpose] = await Promise.all([
249
254
  this.#getWalletAccount(walletAccount),
250
255
  purpose === undefined
251
- ? await this.getSupportedPurposes({ assetName, walletAccount })
256
+ ? this.#assetSources.getDefaultPurpose({ assetName, walletAccount })
252
257
  : undefined,
253
258
  ])
254
259
 
255
260
  if (purpose === undefined) {
256
- ;[purpose] = purposes
261
+ purpose = defaultPurpose
257
262
  }
258
263
 
264
+ const asset = this.#assetsModule.getAsset(assetName)
259
265
  const keyIdentifier = asset.baseAsset.api.getKeyIdentifier({
260
266
  purpose,
261
267
  accountIndex: walletAccountInstance.index,
@@ -322,7 +328,7 @@ class AssetClientInterface {
322
328
  })
323
329
  }
324
330
 
325
- saveUnusedAddressIndexes = async () => {
331
+ saveUnusedAddressIndexes = async ({ assetName, walletAccount, changedUnusedAddressIndexes }) => {
326
332
  // no op!! getUnusedAddressIndexes loads from tx log, not from storage
327
333
  }
328
334
 
@@ -17,6 +17,7 @@ import {
17
17
  } from './constants.js'
18
18
  import { getAssetFromAssetId, getFetchErrorMessage, isDisabledCustomToken } from './utils.js'
19
19
  import createFetchival from '@exodus/fetch/create-fetchival'
20
+ import { validateCustomToken, isValidCustomToken } from '@exodus/asset-schema-validation'
20
21
 
21
22
  const { get, isEmpty, once, uniq } = lodash
22
23
 
@@ -78,7 +79,7 @@ export class AssetsModule {
78
79
  #customTokensServerUrl
79
80
  #updateableProps
80
81
  #iconsStorage
81
- #validateCustomToken
82
+ #shouldValidateCustomToken
82
83
  #assetPlugins
83
84
  #combinedAssetsList
84
85
  #storageDataKey
@@ -100,7 +101,6 @@ export class AssetsModule {
100
101
  assetPlugins,
101
102
  assetsAtom,
102
103
  combinedAssetsList = [],
103
- validateCustomToken = () => true,
104
104
  config = {},
105
105
  fetch,
106
106
  logger,
@@ -115,7 +115,7 @@ export class AssetsModule {
115
115
  this.#customTokensServerUrl = config.customTokensServerUrl || CT_DEFAULT_SERVER
116
116
  this.#updateableProps = [...CT_UPDATEABLE_PROPERTIES, 'version']
117
117
  this.#iconsStorage = iconsStorage
118
- this.#validateCustomToken = validateCustomToken
118
+ this.#shouldValidateCustomToken = config.shouldValidateCustomToken ?? true
119
119
  this.#storageDataKey = config.storageDataKey || CT_DATA_KEY
120
120
  this.#storageTimestampKey = config.storageTimestampKey || CT_TIMESTAMP_KEY
121
121
  this.#customTokenUpdateInterval = config.customTokenUpdateInterval || CT_UPDATE_INTERVAL
@@ -223,11 +223,17 @@ export class AssetsModule {
223
223
  }
224
224
 
225
225
  const _token = await fetchTokenAndCacheError()
226
- if (!this.#validateCustomToken(_token)) {
227
- this.#logger.warn('Invalid Custom Token schema')
228
- const err = new Error('Token did not pass validation')
229
- this.#setCache(key, { cachedError: err })
230
- throw err
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
+ }
231
237
  }
232
238
 
233
239
  const token = normalizeToken(_token)
@@ -319,7 +325,7 @@ export class AssetsModule {
319
325
  ? await this.#fetch('tokens', { tokenNames: tokenNamesToFetch }, 'tokens')
320
326
  : []
321
327
 
322
- const validTokens = fetchedTokens.filter(this.#validateCustomToken).map(normalizeToken)
328
+ const validTokens = fetchedTokens.filter(this.#isValidCustomToken).map(normalizeToken)
323
329
  if (validTokens.length !== fetchedTokens.length)
324
330
  this.#logger.warn('Invalid Custom Token schema')
325
331
 
@@ -402,7 +408,7 @@ export class AssetsModule {
402
408
  { baseAssetName: baseAssetNames, lifecycleStatus, query, excludeTags, pageNumber, pageSize },
403
409
  'tokens'
404
410
  )
405
- const validTokens = tokens.filter(this.#validateCustomToken)
411
+ const validTokens = tokens.filter(this.#isValidCustomToken)
406
412
 
407
413
  if (validTokens.length !== tokens.length) this.#logger.warn('Invalid Custom Token schema')
408
414
 
@@ -419,7 +425,7 @@ export class AssetsModule {
419
425
 
420
426
  #fetchUpdates = async (assetVersions) => {
421
427
  const updatedTokens = await this.#fetch('updates', assetVersions, 'tokens')
422
- const validatedTokens = updatedTokens.filter(this.#validateCustomToken).map(normalizeToken)
428
+ const validatedTokens = updatedTokens.filter(this.#isValidCustomToken).map(normalizeToken)
423
429
  if (validatedTokens.length !== updatedTokens.length) {
424
430
  this.#logger.warn('Invalid Custom Token schema')
425
431
  }
@@ -626,6 +632,14 @@ export class AssetsModule {
626
632
  return !lastUpdateDate || Date.now() - lastUpdateDate > this.#customTokenUpdateInterval
627
633
  }
628
634
 
635
+ #isValidCustomToken = (token) => {
636
+ if (!this.#shouldValidateCustomToken) {
637
+ return true
638
+ }
639
+
640
+ return isValidCustomToken(token)
641
+ }
642
+
629
643
  clear = async () =>
630
644
  this.#storage &&
631
645
  Promise.all([
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@exodus/assets-feature",
3
- "version": "7.3.0",
3
+ "version": "7.4.1",
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",
7
7
  "main": "index.js",
8
8
  "author": "Exodus Movement, Inc.",
9
9
  "scripts": {
10
- "test": "run -T exodus-test --jest --esbuild",
10
+ "test": "run -T exodus-test --jest",
11
11
  "lint": "run -T eslint .",
12
12
  "lint:fix": "yarn lint --fix"
13
13
  },
@@ -36,6 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@exodus/asset-lib": "^5.3.0",
39
+ "@exodus/asset-schema-validation": "^1.0.1",
39
40
  "@exodus/assets": "^11.3.0",
40
41
  "@exodus/atoms": "^9.0.0",
41
42
  "@exodus/basic-utils": "^4.0.0",
@@ -64,12 +65,12 @@
64
65
  "@exodus/fusion-local": "^2.1.0",
65
66
  "@exodus/keychain": "^7.3.0",
66
67
  "@exodus/logger": "^1.2.3",
67
- "@exodus/models": "^12.8.2",
68
+ "@exodus/models": "^12.12.0",
68
69
  "@exodus/osmosis-plugin": "^1.3.3",
69
70
  "@exodus/public-key-provider": "^4.1.1",
70
71
  "@exodus/redux-dependency-injection": "^4.1.1",
71
72
  "@exodus/storage-memory": "^2.2.2",
72
- "@exodus/wallet-accounts": "^17.5.1",
73
+ "@exodus/wallet-accounts": "^17.5.2",
73
74
  "@exodus/wild-emitter": "^1.0.0",
74
75
  "bip39": "^3.1.0",
75
76
  "events": "^3.3.0",
@@ -79,5 +80,5 @@
79
80
  "publishConfig": {
80
81
  "access": "public"
81
82
  },
82
- "gitHead": "272804211316a2713c003e48672dc60d8fb919f0"
83
+ "gitHead": "45dd4907f5d65078978854be2eb397e479730c85"
83
84
  }
package/plugin/index.js CHANGED
@@ -80,6 +80,7 @@ const createAssetsPlugin = ({
80
80
  const onStop = () => {
81
81
  observers.forEach((observer) => observer.unregister())
82
82
  subscribers.forEach((unsubscribe) => unsubscribe())
83
+ customTokensMonitor?.stop()
83
84
  }
84
85
 
85
86
  const onClear = async () => {