@exodus/assets-feature 1.5.0 → 1.7.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 CHANGED
@@ -3,6 +3,206 @@
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
+ ## [1.7.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@1.6.0...@exodus/assets-feature@1.7.0) (2023-09-11)
7
+
8
+ ### Features
9
+
10
+ - Ability to remotely make Custom Tokens active ([#4163](https://github.com/ExodusMovement/exodus-hydra/issues/4163)) ([3e73d36](https://github.com/ExodusMovement/exodus-hydra/commit/3e73d3624df152a76e3b5aa00a800b6bb12669cf))
11
+ - ADA custom-token support ([#3195](https://github.com/ExodusMovement/exodus-hydra/issues/3195)) ([bbccbab](https://github.com/ExodusMovement/exodus-hydra/commit/bbccbabacccc350d0a5df9ae120bc4ffe17b1709))
12
+ - ADA unlock collateral & reserve messaging ([#4007](https://github.com/ExodusMovement/exodus-hydra/issues/4007)) ([7af0fce](https://github.com/ExodusMovement/exodus-hydra/commit/7af0fcec46da94b1f0033552499378ad5f1755e9))
13
+ - add #/assets module ([#14](https://github.com/ExodusMovement/exodus-hydra/issues/14)) ([8461cf2](https://github.com/ExodusMovement/exodus-hydra/commit/8461cf22d50ab63a9e695254811784a3963a1888))
14
+ - add ALGO asset ([#955](https://github.com/ExodusMovement/exodus-hydra/issues/955)) ([ca48030](https://github.com/ExodusMovement/exodus-hydra/commit/ca48030213d86f97cd653cce70069778a8509e34))
15
+ - add ALGO tokens ([#1071](https://github.com/ExodusMovement/exodus-hydra/issues/1071)) ([1040e12](https://github.com/ExodusMovement/exodus-hydra/commit/1040e12d329a1be2124a97304f153c118e4ad443))
16
+ - add arbitrum one and arbitrum nova ([#3540](https://github.com/ExodusMovement/exodus-hydra/issues/3540)) ([86dc056](https://github.com/ExodusMovement/exodus-hydra/commit/86dc056a023554aa91cc01de0a479fe1e3143ec6))
17
+ - add arbitrum token bundle ([#3828](https://github.com/ExodusMovement/exodus-hydra/issues/3828)) ([fbb40e7](https://github.com/ExodusMovement/exodus-hydra/commit/fbb40e7460401f47125408e782987ba464eef012))
18
+ - add asset features directly to asset.api ([#5032](https://github.com/ExodusMovement/exodus-hydra/issues/5032)) ([5c65efd](https://github.com/ExodusMovement/exodus-hydra/commit/5c65efd3d29c223f44bfec41149ae36915543414))
19
+ - add assets module definition ([#3931](https://github.com/ExodusMovement/exodus-hydra/issues/3931)) ([3157f44](https://github.com/ExodusMovement/exodus-hydra/commit/3157f44de260fa4c2c48dda311f907c114b07088))
20
+ - add assetsModule.getAsset(s) ([#717](https://github.com/ExodusMovement/exodus-hydra/issues/717)) ([d662a7c](https://github.com/ExodusMovement/exodus-hydra/commit/d662a7ce079a60926cac368bfe11a43fdfb26679))
21
+ - Add AURORA Send/Receive ([#4403](https://github.com/ExodusMovement/exodus-hydra/issues/4403)) ([a654287](https://github.com/ExodusMovement/exodus-hydra/commit/a6542875e8cd186e18ac0be7247608a9de3971b9))
22
+ - add balance chart selectors ([#235](https://github.com/ExodusMovement/exodus-hydra/issues/235)) ([8f50bcd](https://github.com/ExodusMovement/exodus-hydra/commit/8f50bcd798a1d15938910c59d4dfaf1af428aab1))
23
+ - Add BASE Send/Receive ([#4529](https://github.com/ExodusMovement/exodus-hydra/issues/4529)) ([df7ad5a](https://github.com/ExodusMovement/exodus-hydra/commit/df7ad5aa29fede922c237cfbf9c9ed93e7660681))
24
+ - add basemainnet to defaultEnabledAssetsList ([#4865](https://github.com/ExodusMovement/exodus-hydra/issues/4865)) ([107ae2f](https://github.com/ExodusMovement/exodus-hydra/commit/107ae2f4077013ad1ac8741a07034ba94898684f))
25
+ - add basic ethereum api with eth shared monitor ([#662](https://github.com/ExodusMovement/exodus-hydra/issues/662)) ([765d7c6](https://github.com/ExodusMovement/exodus-hydra/commit/765d7c68c827f8b7ed6c38853f76101a950e2d06)), closes [#698](https://github.com/ExodusMovement/exodus-hydra/issues/698)
26
+ - add basic FTM support in ExoDEX ([#2319](https://github.com/ExodusMovement/exodus-hydra/issues/2319)) ([a0c7fc2](https://github.com/ExodusMovement/exodus-hydra/commit/a0c7fc2ce05b8ad4d06bd8512ffc70cfd63ead92))
27
+ - add custom token icon support ([#886](https://github.com/ExodusMovement/exodus-hydra/issues/886)) ([a030abe](https://github.com/ExodusMovement/exodus-hydra/commit/a030abe04b6d63a9277bff77c1cdcd3044714471))
28
+ - add custom token metadata ([#423](https://github.com/ExodusMovement/exodus-hydra/issues/423)) ([60e2ab5](https://github.com/ExodusMovement/exodus-hydra/commit/60e2ab5e6b017c1a056a6aca4055c4d3a2814fbb))
29
+ - add custom tokens support for arbitrum ([#3770](https://github.com/ExodusMovement/exodus-hydra/issues/3770)) ([b4c8ba9](https://github.com/ExodusMovement/exodus-hydra/commit/b4c8ba9442d771dd22aed731604b47c8dffd0cf4))
30
+ - add eth built-in assets to state ([#677](https://github.com/ExodusMovement/exodus-hydra/issues/677)) ([e799136](https://github.com/ExodusMovement/exodus-hydra/commit/e799136b1049d0703ed0d9207a708ba3fc9808ba))
31
+ - add ETH fee selectors ([#682](https://github.com/ExodusMovement/exodus-hydra/issues/682)) ([eadcc3b](https://github.com/ExodusMovement/exodus-hydra/commit/eadcc3b543e002b02f88d51cb4e701dcb92e9bb4))
32
+ - add ETH send support ([#697](https://github.com/ExodusMovement/exodus-hydra/issues/697)) ([df323c8](https://github.com/ExodusMovement/exodus-hydra/commit/df323c862feda9a3a8b6c3400d65257885fefdc9))
33
+ - add getEstimatedGas, getGasPrice and getNonce eth deps ([#873](https://github.com/ExodusMovement/exodus-hydra/issues/873)) ([d05bd25](https://github.com/ExodusMovement/exodus-hydra/commit/d05bd257be09cb37b98def32438f6df2ae961905))
34
+ - add getFee() for algorand ([#3054](https://github.com/ExodusMovement/exodus-hydra/issues/3054)) ([50b714f](https://github.com/ExodusMovement/exodus-hydra/commit/50b714f08ef47f1f3dba2ce54188845a578518e3))
35
+ - add nile build ([#1336](https://github.com/ExodusMovement/exodus-hydra/issues/1336)) ([883be80](https://github.com/ExodusMovement/exodus-hydra/commit/883be806a1021dfaf95dd7075dd6e4cd521f2751))
36
+ - add optimism custom token support ([#4062](https://github.com/ExodusMovement/exodus-hydra/issues/4062)) ([81885d0](https://github.com/ExodusMovement/exodus-hydra/commit/81885d0911a05a74c24abc6e2570008320608521))
37
+ - add optimism support ([#4027](https://github.com/ExodusMovement/exodus-hydra/issues/4027)) ([8f7fe92](https://github.com/ExodusMovement/exodus-hydra/commit/8f7fe92758bfd3bf6cebbec0fe3ac9baa372aff3))
38
+ - add optimism token bundle ([#4197](https://github.com/ExodusMovement/exodus-hydra/issues/4197)) ([b366ee1](https://github.com/ExodusMovement/exodus-hydra/commit/b366ee15e6283a80205be09c8d5a2ebfbc959c79))
39
+ - add rates monitor ([#50](https://github.com/ExodusMovement/exodus-hydra/issues/50)) ([5f0a760](https://github.com/ExodusMovement/exodus-hydra/commit/5f0a760a2359e61b70cf5a82eff8250fd40f9b29))
40
+ - Add Rootstock ([#3624](https://github.com/ExodusMovement/exodus-hydra/issues/3624)) ([e53c7b5](https://github.com/ExodusMovement/exodus-hydra/commit/e53c7b5723d7ee6e2e3255dc346799d5800323af))
41
+ - add SOL unknown tokens to wallet ([#541](https://github.com/ExodusMovement/exodus-hydra/issues/541)) ([06d27fb](https://github.com/ExodusMovement/exodus-hydra/commit/06d27fb7e8a0d5cbaece9d393fd9cfa440e9f462))
42
+ - add Solana monitor ([#16](https://github.com/ExodusMovement/exodus-hydra/issues/16)) ([0e254b5](https://github.com/ExodusMovement/exodus-hydra/commit/0e254b59bd0f950bb105012a0b7c029cbb337d3c))
43
+ - add Sovryn DLLR (RSK) ([#4582](https://github.com/ExodusMovement/exodus-hydra/issues/4582)) ([2f01de8](https://github.com/ExodusMovement/exodus-hydra/commit/2f01de847a3ac0e4c2c25603e5851cd68d8098d6))
44
+ - add STATUS.DISABLED for custom tokens ([#2665](https://github.com/ExodusMovement/exodus-hydra/issues/2665)) ([205052a](https://github.com/ExodusMovement/exodus-hydra/commit/205052af74a7236f877b3d597ca5226cf1b31dd6))
45
+ - add support for AVAX, BSC, FTM, MATIC base assets (dev) ([#1210](https://github.com/ExodusMovement/exodus-hydra/issues/1210)) ([20d0f8b](https://github.com/ExodusMovement/exodus-hydra/commit/20d0f8b6f78b57151772975c196a4c9e0667aced))
46
+ - add support for Cardano (ADA) ([#2891](https://github.com/ExodusMovement/exodus-hydra/issues/2891)) ([3977d31](https://github.com/ExodusMovement/exodus-hydra/commit/3977d31b67b36070df9ef56893fda8771cb3afbc))
47
+ - add txLog ([#17](https://github.com/ExodusMovement/exodus-hydra/issues/17)) ([71af1df](https://github.com/ExodusMovement/exodus-hydra/commit/71af1df6364c74863970d1b80e8597e91851dd99))
48
+ - add Unverified tokens ([#862](https://github.com/ExodusMovement/exodus-hydra/issues/862)) ([5061406](https://github.com/ExodusMovement/exodus-hydra/commit/5061406e29ad0b984875bd4bbf10fdbe1ca34c9d))
49
+ - add USDT and USDC Algo to available assets ([#2040](https://github.com/ExodusMovement/exodus-hydra/issues/2040)) ([b6c32f5](https://github.com/ExodusMovement/exodus-hydra/commit/b6c32f5b3c560773ffe5bed4aa85ce49350fab8a))
50
+ - add wallet history screen ([#126](https://github.com/ExodusMovement/exodus-hydra/issues/126)) ([28bd951](https://github.com/ExodusMovement/exodus-hydra/commit/28bd951c1851098c2a707aba20477fd3703dbd06))
51
+ - adding btc send (v2) ([#3036](https://github.com/ExodusMovement/exodus-hydra/issues/3036)) ([95e8db8](https://github.com/ExodusMovement/exodus-hydra/commit/95e8db8688811e3bdc5fe1197cf452e3068070c9))
52
+ - asset plugin architecture ([#4712](https://github.com/ExodusMovement/exodus-hydra/issues/4712)) ([dace647](https://github.com/ExodusMovement/exodus-hydra/commit/dace647f51b6174711f76e9a15409202ae8d12db))
53
+ - auto detect received ethereum tokens ([#789](https://github.com/ExodusMovement/exodus-hydra/issues/789)) ([96afee8](https://github.com/ExodusMovement/exodus-hydra/commit/96afee8af301cfd6842a834c1333cca533158469))
54
+ - automatically add tokens from contract transactions ([#516](https://github.com/ExodusMovement/exodus-hydra/issues/516)) ([c564eef](https://github.com/ExodusMovement/exodus-hydra/commit/c564eef8887d630cfb563d2f1586559e994a6fae))
55
+ - balances for history based assets (e.g. ethereum) ([#736](https://github.com/ExodusMovement/exodus-hydra/issues/736)) ([39f0f9d](https://github.com/ExodusMovement/exodus-hydra/commit/39f0f9d376a728d32f9c16304821d9fab98484ee))
56
+ - balances module ([#513](https://github.com/ExodusMovement/exodus-hydra/issues/513)) ([8ba1794](https://github.com/ExodusMovement/exodus-hydra/commit/8ba1794e3fd316577ea938b79e21e763727efa26))
57
+ - basic cosmos send, receive, history ([#3911](https://github.com/ExodusMovement/exodus-hydra/issues/3911)) ([6b8bdf9](https://github.com/ExodusMovement/exodus-hydra/commit/6b8bdf9697ee0a07ea0a2d513014e76a47e8410f))
58
+ - basic osmosis send, receive, history ([#4075](https://github.com/ExodusMovement/exodus-hydra/issues/4075)) ([3c86e17](https://github.com/ExodusMovement/exodus-hydra/commit/3c86e178f641115707d9eeceadaf151cc5e3393a))
59
+ - bitcoin in production ([#3139](https://github.com/ExodusMovement/exodus-hydra/issues/3139)) ([14e4099](https://github.com/ExodusMovement/exodus-hydra/commit/14e4099adbf748ab65fe801a2af793e6dc61a5c4))
60
+ - bitcoin, bitcointestnet, bitcoinregtest v1 (1) ([#2779](https://github.com/ExodusMovement/exodus-hydra/issues/2779)) ([65fd425](https://github.com/ExodusMovement/exodus-hydra/commit/65fd425220d74e30c176e483cae8557b96ea8754))
61
+ - bitcointestnet/bitcoinregtest as bitcoin ([#3687](https://github.com/ExodusMovement/exodus-hydra/issues/3687)) ([e858597](https://github.com/ExodusMovement/exodus-hydra/commit/e858597c633817bc6e555bc5b01a743f0e3634a5))
62
+ - cache fetches from CT service ([#533](https://github.com/ExodusMovement/exodus-hydra/issues/533)) ([85bc2a5](https://github.com/ExodusMovement/exodus-hydra/commit/85bc2a542f0da679a37f76fb6fc30f9fcbf3300a))
63
+ - combined assets ui in manage assets ([#837](https://github.com/ExodusMovement/exodus-hydra/issues/837)) ([dc82c39](https://github.com/ExodusMovement/exodus-hydra/commit/dc82c39cee8ea7e14042bd01fbda388b7ee8e5e8))
64
+ - combined ethereum ([#4214](https://github.com/ExodusMovement/exodus-hydra/issues/4214)) ([4e99d35](https://github.com/ExodusMovement/exodus-hydra/commit/4e99d359c0af188a863e3fc47e74038aa563d44a))
65
+ - **ct:** Add ability to test Custom Tokens data when curating ([#3399](https://github.com/ExodusMovement/exodus-hydra/issues/3399)) ([24b0574](https://github.com/ExodusMovement/exodus-hydra/commit/24b05747fd449f62e9b6ce6dbe729868c40428ef))
66
+ - custom token prerequisites ([#270](https://github.com/ExodusMovement/exodus-hydra/issues/270)) ([5274cae](https://github.com/ExodusMovement/exodus-hydra/commit/5274cae4575b9aa8423677e816937f07f24bb847))
67
+ - decouple assets module test from client ([#5067](https://github.com/ExodusMovement/exodus-hydra/issues/5067)) ([8039d29](https://github.com/ExodusMovement/exodus-hydra/commit/8039d29ce80f02571c4db89e3fd6937e91aa956a))
68
+ - Delete remote custom token icons from storage when not used ([#4335](https://github.com/ExodusMovement/exodus-hydra/issues/4335)) ([73383ab](https://github.com/ExodusMovement/exodus-hydra/commit/73383ab042f1a2046bd62b202c0d8656d4705a26))
69
+ - dynamic tokens in Solana monitor ([#150](https://github.com/ExodusMovement/exodus-hydra/issues/150)) ([6d60339](https://github.com/ExodusMovement/exodus-hydra/commit/6d60339733d3f32fe5c30c36d682878a36078e1e))
70
+ - enable ADA in production ([#2984](https://github.com/ExodusMovement/exodus-hydra/issues/2984)) ([de3c95b](https://github.com/ExodusMovement/exodus-hydra/commit/de3c95bb8f1ebf35ea142538cde961645f3b57bd))
71
+ - enable ALGO custom tokens ([#1198](https://github.com/ExodusMovement/exodus-hydra/issues/1198)) ([024aedd](https://github.com/ExodusMovement/exodus-hydra/commit/024aedd33491fa5d45d2b170cccc221e8276a62c))
72
+ - enable Algorand in prod ([#1683](https://github.com/ExodusMovement/exodus-hydra/issues/1683)) ([a38f75c](https://github.com/ExodusMovement/exodus-hydra/commit/a38f75cb2248150f8cdcaf55de47ceedc6cb3105))
73
+ - enable arbitrum assets in prod ([#3857](https://github.com/ExodusMovement/exodus-hydra/issues/3857)) ([0e10d15](https://github.com/ExodusMovement/exodus-hydra/commit/0e10d1538f46064ede31277a4fea16bfeedab36f))
74
+ - enable arbitrum networks in prod ([#3733](https://github.com/ExodusMovement/exodus-hydra/issues/3733)) ([87f691a](https://github.com/ExodusMovement/exodus-hydra/commit/87f691acd9eaacaa2b456fcc1dee5c09762721b0))
75
+ - Enable AURORA Custom Tokens ([#4456](https://github.com/ExodusMovement/exodus-hydra/issues/4456)) ([fb112ca](https://github.com/ExodusMovement/exodus-hydra/commit/fb112cab7a71235ffa193121881c1501c421c9e5))
76
+ - Enable AURORA to PROD ([#4458](https://github.com/ExodusMovement/exodus-hydra/issues/4458)) ([11b9e36](https://github.com/ExodusMovement/exodus-hydra/commit/11b9e364a5f70f2e6945e72e04a750f720373d3d))
77
+ - enable AVAX in prod ([#2368](https://github.com/ExodusMovement/exodus-hydra/issues/2368)) ([737d184](https://github.com/ExodusMovement/exodus-hydra/commit/737d1848094aab630ad1821d96d75dc93d4a6b74))
78
+ - enable BASE custom tokens ([#4559](https://github.com/ExodusMovement/exodus-hydra/issues/4559)) ([956ea5e](https://github.com/ExodusMovement/exodus-hydra/commit/956ea5e1d0997db3bd42842de6cabc614660cc02))
79
+ - Enable BASE mainnet to PROD (backport) ([#4793](https://github.com/ExodusMovement/exodus-hydra/issues/4793)) ([3228627](https://github.com/ExodusMovement/exodus-hydra/commit/3228627c4103d1c8eb59965ff4222f30cf68c431))
80
+ - enable BSC asset and support it in wallet ([#2108](https://github.com/ExodusMovement/exodus-hydra/issues/2108)) ([bc15600](https://github.com/ExodusMovement/exodus-hydra/commit/bc15600f111a524a43f00d5ec8fac2c01cd264b8))
81
+ - enable BSC custom tokens in prod ([#2246](https://github.com/ExodusMovement/exodus-hydra/issues/2246)) ([ddc06c4](https://github.com/ExodusMovement/exodus-hydra/commit/ddc06c4aef62b0950543506517c4a9fb814ceb34))
82
+ - enable DAI, USDC, USDT, LINK on Ethereum ([#777](https://github.com/ExodusMovement/exodus-hydra/issues/777)) ([7cc3d4e](https://github.com/ExodusMovement/exodus-hydra/commit/7cc3d4eb38e4871526d3cf0f32cc36fb44858cb7))
83
+ - Enable FLARE asset to PROD ([#4345](https://github.com/ExodusMovement/exodus-hydra/issues/4345)) ([f27489f](https://github.com/ExodusMovement/exodus-hydra/commit/f27489f009ce5597edccb6a6ae69f2a0deb27184))
84
+ - Enable Flare Custom Tokens ([#4291](https://github.com/ExodusMovement/exodus-hydra/issues/4291)) ([c5f3e52](https://github.com/ExodusMovement/exodus-hydra/commit/c5f3e52f8ad2e85207b23ed28fd31bf0d5b6ebf9))
85
+ - enable matic as default asset ([#1997](https://github.com/ExodusMovement/exodus-hydra/issues/1997)) ([eaed1ed](https://github.com/ExodusMovement/exodus-hydra/commit/eaed1ed4226c810f2b23a98630eff61947e4ac14))
86
+ - enable optimism in prod ([#4102](https://github.com/ExodusMovement/exodus-hydra/issues/4102)) ([940a1ba](https://github.com/ExodusMovement/exodus-hydra/commit/940a1babe607110190659f5e4c06c1dcf0953acf))
87
+ - enable Polygon custom tokens in prod ([#2005](https://github.com/ExodusMovement/exodus-hydra/issues/2005)) ([50f49fd](https://github.com/ExodusMovement/exodus-hydra/commit/50f49fd29defd9f6e193540a43ae64a72d2dae3f))
88
+ - enable Polygon in prod ([#1849](https://github.com/ExodusMovement/exodus-hydra/issues/1849)) ([6cf44ef](https://github.com/ExodusMovement/exodus-hydra/commit/6cf44eff7844ee0affb3bc8e6d0baf2225c8c0cc))
89
+ - Enable Rootstock Custom Tokens ([#3782](https://github.com/ExodusMovement/exodus-hydra/issues/3782)) ([fd9b5ca](https://github.com/ExodusMovement/exodus-hydra/commit/fd9b5caee5a056862b3a062e1137c36cb41cc3ad))
90
+ - Enable Rootstock for Prod ([#3729](https://github.com/ExodusMovement/exodus-hydra/issues/3729)) ([b0b2c8f](https://github.com/ExodusMovement/exodus-hydra/commit/b0b2c8f17f0e7e0651b49237f0292a02ef8e7e9e))
91
+ - enhance asset (de)serialization ([#3103](https://github.com/ExodusMovement/exodus-hydra/issues/3103)) ([15674ae](https://github.com/ExodusMovement/exodus-hydra/commit/15674aea753eac1653ae3e2ccfd7d93170517bae))
92
+ - EVM createAssetFactory ([#4776](https://github.com/ExodusMovement/exodus-hydra/issues/4776)) ([beb5aa2](https://github.com/ExodusMovement/exodus-hydra/commit/beb5aa2a87e6beeca27e96d1196233a6625264be))
93
+ - **exodex:** add AVAX default asset ([#2278](https://github.com/ExodusMovement/exodus-hydra/issues/2278)) ([7a41d29](https://github.com/ExodusMovement/exodus-hydra/commit/7a41d297c4e6367f0eda68197e65f9df3d472544))
94
+ - **exodex:** enable binance smart chain swaps ([#2080](https://github.com/ExodusMovement/exodus-hydra/issues/2080)) ([85be19e](https://github.com/ExodusMovement/exodus-hydra/commit/85be19e4926791bb3c45676baa900338d5338c8b))
95
+ - **exodex:** matic swap in UI ([#1819](https://github.com/ExodusMovement/exodus-hydra/issues/1819)) ([fda1a3a](https://github.com/ExodusMovement/exodus-hydra/commit/fda1a3af72c8b5909561c2d5fcdb40aad529a023)), closes [#1919](https://github.com/ExodusMovement/exodus-hydra/issues/1919)
96
+ - **exodex:** move business logic to sw ([#991](https://github.com/ExodusMovement/exodus-hydra/issues/991)) ([25faced](https://github.com/ExodusMovement/exodus-hydra/commit/25faced17d3dd5e8e66dd3a3197e67f11815afef)), closes [#1005](https://github.com/ExodusMovement/exodus-hydra/issues/1005) [#1021](https://github.com/ExodusMovement/exodus-hydra/issues/1021) [#1028](https://github.com/ExodusMovement/exodus-hydra/issues/1028) [#1035](https://github.com/ExodusMovement/exodus-hydra/issues/1035) [#1036](https://github.com/ExodusMovement/exodus-hydra/issues/1036) [#1056](https://github.com/ExodusMovement/exodus-hydra/issues/1056)
97
+ - **exodex:** swap on avalanchec network ([#2311](https://github.com/ExodusMovement/exodus-hydra/issues/2311)) ([7b81d5a](https://github.com/ExodusMovement/exodus-hydra/commit/7b81d5ad2ecb18e63b6bdff7bbc2e04d52495539))
98
+ - extend assets api to export key identifier metadata ([#2166](https://github.com/ExodusMovement/exodus-hydra/issues/2166)) ([4f9e242](https://github.com/ExodusMovement/exodus-hydra/commit/4f9e242119a086fbc7df4557aaa80de4f8ff7f7d))
99
+ - factor out availableAssetModule ([#1924](https://github.com/ExodusMovement/exodus-hydra/issues/1924)) ([8e60e95](https://github.com/ExodusMovement/exodus-hydra/commit/8e60e95a1ec7604c346b3e54acedd80b0463f0f9))
100
+ - fantom custom tokens in prod ([#2557](https://github.com/ExodusMovement/exodus-hydra/issues/2557)) ([7ca49e4](https://github.com/ExodusMovement/exodus-hydra/commit/7ca49e4c0c1af1411185838f4e03659d9e0a6f36))
101
+ - feature flag assets ([#1066](https://github.com/ExodusMovement/exodus-hydra/issues/1066)) ([2f8b1f9](https://github.com/ExodusMovement/exodus-hydra/commit/2f8b1f927281392bbfa0886360764d3786a25fd5))
102
+ - fetch staking data in monitor ([#49](https://github.com/ExodusMovement/exodus-hydra/issues/49)) ([0b5b4d9](https://github.com/ExodusMovement/exodus-hydra/commit/0b5b4d916801f40b79eebd43364aa1827ae4f463))
103
+ - fiat transactions history ui ([#421](https://github.com/ExodusMovement/exodus-hydra/issues/421)) ([52c819d](https://github.com/ExodusMovement/exodus-hydra/commit/52c819d98890a8781a9ab372c9a37a6ba0e36396))
104
+ - filter out offensive tokens ([#3258](https://github.com/ExodusMovement/exodus-hydra/issues/3258)) ([97df94f](https://github.com/ExodusMovement/exodus-hydra/commit/97df94f6955a6510bbb5d314dde1d41f61303f80))
105
+ - Flare Send/Receive ([#4290](https://github.com/ExodusMovement/exodus-hydra/issues/4290)) ([7bc3567](https://github.com/ExodusMovement/exodus-hydra/commit/7bc3567d77d4a962fb4481051b971b3c4a604bc9))
106
+ - goerli evm ([#1863](https://github.com/ExodusMovement/exodus-hydra/issues/1863)) ([6ad6fff](https://github.com/ExodusMovement/exodus-hydra/commit/6ad6fffe2678bf12cd2bcfaf716a81fecd2a08f1))
107
+ - handle ADA utxo reserved collateral ([#3367](https://github.com/ExodusMovement/exodus-hydra/issues/3367)) ([395fcbf](https://github.com/ExodusMovement/exodus-hydra/commit/395fcbf171e26c08956b0802f8e81647aa361ff6))
108
+ - handle Contract transaction type ([#399](https://github.com/ExodusMovement/exodus-hydra/issues/399)) ([76e08de](https://github.com/ExodusMovement/exodus-hydra/commit/76e08de3d1689506e2ac08faaf7138afe4ba23af))
109
+ - hide ADA tokens behind feature flag ([#3311](https://github.com/ExodusMovement/exodus-hydra/issues/3311)) ([fd3e3c6](https://github.com/ExodusMovement/exodus-hydra/commit/fd3e3c6ed96b4a6df934eba4fee64c047155734a))
110
+ - implement get-spendable-balance selector ([#343](https://github.com/ExodusMovement/exodus-hydra/issues/343)) ([5d6ebcb](https://github.com/ExodusMovement/exodus-hydra/commit/5d6ebcb515dcb0cc309fb6eeec5669233236c26a))
111
+ - initial custom tokens support ([#323](https://github.com/ExodusMovement/exodus-hydra/issues/323)) ([59bae65](https://github.com/ExodusMovement/exodus-hydra/commit/59bae655b17b2aaaf85f69d6fc3935ada514046c))
112
+ - integrate cosmos provider ([#4288](https://github.com/ExodusMovement/exodus-hydra/issues/4288)) ([cc3af7b](https://github.com/ExodusMovement/exodus-hydra/commit/cc3af7b57b8d08ead19c536662635dd8f65a23bd))
113
+ - load assets in UI through RPC ([#280](https://github.com/ExodusMovement/exodus-hydra/issues/280)) ([8afa7d4](https://github.com/ExodusMovement/exodus-hydra/commit/8afa7d4af6a7bb94fa00cb27ca639e1f39ebac9f))
114
+ - make fantommainnet default enabled ([#4613](https://github.com/ExodusMovement/exodus-hydra/issues/4613)) ([a40d84f](https://github.com/ExodusMovement/exodus-hydra/commit/a40d84fd86154f79521916111dcfab8e34df44bd))
115
+ - manage asset tags ([#977](https://github.com/ExodusMovement/exodus-hydra/issues/977)) ([c5a4756](https://github.com/ExodusMovement/exodus-hydra/commit/c5a4756683716247d50311213954416b74661338))
116
+ - manage assets screen ([#703](https://github.com/ExodusMovement/exodus-hydra/issues/703)) ([1711adf](https://github.com/ExodusMovement/exodus-hydra/commit/1711adff508341c2c3bcd536deef9c56ec1498ae))
117
+ - **nfts:** add ability to send Algorand NFTs ([#1552](https://github.com/ExodusMovement/exodus-hydra/issues/1552)) ([cc3628d](https://github.com/ExodusMovement/exodus-hydra/commit/cc3628ddc365aa16d90bb768d5e07a2b5aee9ec3)), closes [#1661](https://github.com/ExodusMovement/exodus-hydra/issues/1661)
118
+ - **nfts:** add support for sending solana pNFTs ([#4315](https://github.com/ExodusMovement/exodus-hydra/issues/4315)) ([672f8b4](https://github.com/ExodusMovement/exodus-hydra/commit/672f8b4d3190f28d6e0df72cb030d7792e1d8c57))
119
+ - **nfts:** receive and send cardano nfts ([#3225](https://github.com/ExodusMovement/exodus-hydra/issues/3225)) ([4c46349](https://github.com/ExodusMovement/exodus-hydra/commit/4c4634951a32bccd569a85815c8b3cc29c9a7c02))
120
+ - pass Ethereum deps to Apps RPC ([#715](https://github.com/ExodusMovement/exodus-hydra/issues/715)) ([4315497](https://github.com/ExodusMovement/exodus-hydra/commit/43154975fbe67a75cbf6c549dc6f39a7377fbced))
121
+ - polygon staking selectors ([#4824](https://github.com/ExodusMovement/exodus-hydra/issues/4824)) ([91f6cbf](https://github.com/ExodusMovement/exodus-hydra/commit/91f6cbf9a73502f37f37fb4315dc5bc2cd3346a3))
122
+ - polygon staking UI integration ([#4900](https://github.com/ExodusMovement/exodus-hydra/issues/4900)) ([55c8df1](https://github.com/ExodusMovement/exodus-hydra/commit/55c8df114099d129a69a706db0362996d68981ad))
123
+ - **polygon-staking:** track everstake delegations ([#5030](https://github.com/ExodusMovement/exodus-hydra/issues/5030)) ([2a7cbe4](https://github.com/ExodusMovement/exodus-hydra/commit/2a7cbe452efb196f58bbbc2525c1a48da18025dc))
124
+ - **polygon:** core logic ([#4806](https://github.com/ExodusMovement/exodus-hydra/issues/4806)) ([d53ffe2](https://github.com/ExodusMovement/exodus-hydra/commit/d53ffe2e212fb1ec5f9b0747d9902e698eb65a41))
125
+ - **referrals:** optin USDC with faucet when KYC verified ([#2709](https://github.com/ExodusMovement/exodus-hydra/issues/2709)) ([aded023](https://github.com/ExodusMovement/exodus-hydra/commit/aded023512192ecaedb00a67176e6989e58a87a7))
126
+ - **referrals:** set dev faucet url ([#2788](https://github.com/ExodusMovement/exodus-hydra/issues/2788)) ([2ba1c1d](https://github.com/ExodusMovement/exodus-hydra/commit/2ba1c1de00b469ddd3bb8690a72a2b324577a5f4))
127
+ - refresh solana monitor on new tokens ([#593](https://github.com/ExodusMovement/exodus-hydra/issues/593)) ([4cebad0](https://github.com/ExodusMovement/exodus-hydra/commit/4cebad017538da775c172cbb0dcfbe2234075897))
128
+ - run monitors in background ([#22](https://github.com/ExodusMovement/exodus-hydra/issues/22)) ([5cda3b3](https://github.com/ExodusMovement/exodus-hydra/commit/5cda3b3cfb8296286871ea277d98877db04dedcb))
129
+ - search custom tokens from ManageAssets screen ([#794](https://github.com/ExodusMovement/exodus-hydra/issues/794)) ([f4ce259](https://github.com/ExodusMovement/exodus-hydra/commit/f4ce259630a8ef3b00f44f93919bbccccaca5e1d))
130
+ - select verified custom tokens from list ([#576](https://github.com/ExodusMovement/exodus-hydra/issues/576)) ([5aaa6fd](https://github.com/ExodusMovement/exodus-hydra/commit/5aaa6fdf0934c9e4b2e82903344f174c47036260))
131
+ - show notification when tx received ([#3924](https://github.com/ExodusMovement/exodus-hydra/issues/3924)) ([c71ec41](https://github.com/ExodusMovement/exodus-hydra/commit/c71ec4104f153817dca8cf1ac24ca19246234d27))
132
+ - show testnets at the bottom ([#4306](https://github.com/ExodusMovement/exodus-hydra/issues/4306)) ([c9143b5](https://github.com/ExodusMovement/exodus-hydra/commit/c9143b53702c307e2e8ec26f95d579c63e71442f))
133
+ - start preparing assets for custom tokens support ([#149](https://github.com/ExodusMovement/exodus-hydra/issues/149)) ([5dbf0bb](https://github.com/ExodusMovement/exodus-hydra/commit/5dbf0bbdfee70b8fdbeba3b778418e7eb0be8b9e))
134
+ - support bumping for ETH tx send ([#3164](https://github.com/ExodusMovement/exodus-hydra/issues/3164)) ([1f3d912](https://github.com/ExodusMovement/exodus-hydra/commit/1f3d912a040d39eb45ceae76b716afa82643238a))
135
+ - temporary assets-module directory prior migration ([#5097](https://github.com/ExodusMovement/exodus-hydra/issues/5097)) ([ac08e3a](https://github.com/ExodusMovement/exodus-hydra/commit/ac08e3a62bfd95078cafb0c1a7fbe45467e593fd))
136
+ - toggle 'enable multichain assets' on ([#1283](https://github.com/ExodusMovement/exodus-hydra/issues/1283)) ([ab62e1f](https://github.com/ExodusMovement/exodus-hydra/commit/ab62e1f5dc9757f752606a81558dbf661f7d2726))
137
+ - trust unverified tokens ([#4096](https://github.com/ExodusMovement/exodus-hydra/issues/4096)) ([7f487c5](https://github.com/ExodusMovement/exodus-hydra/commit/7f487c5958186b20c3cf5e547f2b1b8e3622b51c))
138
+ - update cardano account state after broadcasting tx ([#3615](https://github.com/ExodusMovement/exodus-hydra/issues/3615)) ([f059dd4](https://github.com/ExodusMovement/exodus-hydra/commit/f059dd4f90f2cbc342ea31e5852282f75116d237))
139
+ - update custom tokens ([#903](https://github.com/ExodusMovement/exodus-hydra/issues/903)) ([de0976b](https://github.com/ExodusMovement/exodus-hydra/commit/de0976b6031581182ac9481cb07a73ed31ecfcb8))
140
+ - update default assets (add KIN, USDC, USDT) ([#508](https://github.com/ExodusMovement/exodus-hydra/issues/508)) ([ba513d7](https://github.com/ExodusMovement/exodus-hydra/commit/ba513d79839c5a7b39bf758f6ce62e1028d08cb9))
141
+ - update default enabled assets ([#4475](https://github.com/ExodusMovement/exodus-hydra/issues/4475)) ([934eb26](https://github.com/ExodusMovement/exodus-hydra/commit/934eb26b35b1ae9e3fd788c3f4c9782a1b886af6))
142
+ - wallet screen ([#62](https://github.com/ExodusMovement/exodus-hydra/issues/62)) ([6cb1963](https://github.com/ExodusMovement/exodus-hydra/commit/6cb1963b4c1378e8fca37abc42d27b18f2b2b037))
143
+
144
+ ### Bug Fixes
145
+
146
+ - add `bsc` to `available` assets, display `BSC Total balance` not `BNB` ([#2138](https://github.com/ExodusMovement/exodus-hydra/issues/2138)) ([3a636b2](https://github.com/ExodusMovement/exodus-hydra/commit/3a636b2ef1795f89afff150a3c80520235c2d25d))
147
+ - add ADA & SOL minimum staking amount check ([#4775](https://github.com/ExodusMovement/exodus-hydra/issues/4775)) ([147308e](https://github.com/ExodusMovement/exodus-hydra/commit/147308ef9c886a4881bb0da42ce3c8230eba1b94))
148
+ - add ALGO custom token (with opt-in) ([#1259](https://github.com/ExodusMovement/exodus-hydra/issues/1259)) ([8989ec0](https://github.com/ExodusMovement/exodus-hydra/commit/8989ec0e50641b0aa1b80d6423dddae81517b0d4))
149
+ - Add Custom tokens without icon by address ([#4425](https://github.com/ExodusMovement/exodus-hydra/issues/4425)) ([558c3fd](https://github.com/ExodusMovement/exodus-hydra/commit/558c3fd1561030a23c861a603a2375b869020fe4))
150
+ - add token error message ([#1568](https://github.com/ExodusMovement/exodus-hydra/issues/1568)) ([82524e1](https://github.com/ExodusMovement/exodus-hydra/commit/82524e1ed063d0f2be451ce358dc237a7991fd09))
151
+ - adding built in assets as custom tokens ([#523](https://github.com/ExodusMovement/exodus-hydra/issues/523)) ([1ac9127](https://github.com/ExodusMovement/exodus-hydra/commit/1ac91279f8cd1c72a7ae21d18f548368a3274148))
152
+ - ALGO custom token error ([#1736](https://github.com/ExodusMovement/exodus-hydra/issues/1736)) ([9dd237a](https://github.com/ExodusMovement/exodus-hydra/commit/9dd237aa2a6be2d5f71f80e9f6464b3508e0a185))
153
+ - assets module package.json ([#3924](https://github.com/ExodusMovement/exodus-hydra/issues/3924)) ([8145348](https://github.com/ExodusMovement/exodus-hydra/commit/8145348a4a6b53299e3fa511b6bdd324ddc4ff6e))
154
+ - **assets-features:** tests and lint ([#3925](https://github.com/ExodusMovement/exodus-hydra/issues/3925)) ([c55a2a9](https://github.com/ExodusMovement/exodus-hydra/commit/c55a2a9745d240f7f65f2947682260380e74e96f))
155
+ - build state using dynamic assets ([#648](https://github.com/ExodusMovement/exodus-hydra/issues/648)) ([c55838b](https://github.com/ExodusMovement/exodus-hydra/commit/c55838b8736026234fc39985fc83eec088f78cb0))
156
+ - correctly search for asset by assetId ([#4451](https://github.com/ExodusMovement/exodus-hydra/issues/4451)) ([d8a5624](https://github.com/ExodusMovement/exodus-hydra/commit/d8a56245714a35e4fba70b03eb441bab65e41f2a))
157
+ - correctly separate newly added from present custom tokens ([#2666](https://github.com/ExodusMovement/exodus-hydra/issues/2666)) ([35de7b1](https://github.com/ExodusMovement/exodus-hydra/commit/35de7b1d902948bab8788de0145e682145691a48))
158
+ - custom token add ([#2435](https://github.com/ExodusMovement/exodus-hydra/issues/2435)) ([1c93306](https://github.com/ExodusMovement/exodus-hydra/commit/1c93306bdf60e6b039368a2431e7fea3cf2f1c82))
159
+ - custom tokens search from ManageAssets ([#5001](https://github.com/ExodusMovement/exodus-hydra/issues/5001)) ([aa11c9e](https://github.com/ExodusMovement/exodus-hydra/commit/aa11c9ef4e3a9dc0771795843e35835923da8ca8))
160
+ - define validateAssetId avaxc fantom ([#2161](https://github.com/ExodusMovement/exodus-hydra/issues/2161)) ([1e3d844](https://github.com/ExodusMovement/exodus-hydra/commit/1e3d84428374fa192b985c983524c1bb1f5bc28a))
161
+ - define validateAssetId within BSC api ([#2142](https://github.com/ExodusMovement/exodus-hydra/issues/2142)) ([4a49aff](https://github.com/ExodusMovement/exodus-hydra/commit/4a49affb9d67933c09e19135852850732ade80e3))
162
+ - do not attempt to add unknown tokens already there ([#600](https://github.com/ExodusMovement/exodus-hydra/issues/600)) ([9e14898](https://github.com/ExodusMovement/exodus-hydra/commit/9e14898a41289b0d76590b5b424ce2eab3535eb3))
163
+ - enable Flare support in Exodus provider ([#4347](https://github.com/ExodusMovement/exodus-hydra/issues/4347)) ([4856b4c](https://github.com/ExodusMovement/exodus-hydra/commit/4856b4ca426e5f2466db92a34cdcf9bd3162e4ad))
164
+ - enable isRpcBalance for ETH monitor ([#2090](https://github.com/ExodusMovement/exodus-hydra/issues/2090)) ([8831543](https://github.com/ExodusMovement/exodus-hydra/commit/88315433211d6ca0b62810f5dd9f99b2007492d1))
165
+ - error message when adding an invalid custom token ([#796](https://github.com/ExodusMovement/exodus-hydra/issues/796)) ([1a8c71a](https://github.com/ExodusMovement/exodus-hydra/commit/1a8c71a2865f4cda0f303199cc2630c2be327bef))
166
+ - eth custom fee deserialization issue ([#4253](https://github.com/ExodusMovement/exodus-hydra/issues/4253)) ([4d7b0fd](https://github.com/ExodusMovement/exodus-hydra/commit/4d7b0fd29513576e41238609307c2a557b10ea3d))
167
+ - ETH sending all with hardcoded gas Limit ([#2290](https://github.com/ExodusMovement/exodus-hydra/issues/2290)) ([ab72ba5](https://github.com/ExodusMovement/exodus-hydra/commit/ab72ba5260cbc799f51f2377e3cba8b017a19654))
168
+ - eth-like missing getFee imports after multiple merges ([#3789](https://github.com/ExodusMovement/exodus-hydra/issues/3789)) ([8424e14](https://github.com/ExodusMovement/exodus-hydra/commit/8424e14490f9cf6f465048a6b77dcb36c54bff9b))
169
+ - **eth:** steth balance with rewards ([#1057](https://github.com/ExodusMovement/exodus-hydra/issues/1057)) ([b714896](https://github.com/ExodusMovement/exodus-hydra/commit/b714896ce769c4bf57885a2c24bb14ea5fce50fa))
170
+ - **exodex:** increase eth gas fees ([#3143](https://github.com/ExodusMovement/exodus-hydra/issues/3143)) ([ed35b20](https://github.com/ExodusMovement/exodus-hydra/commit/ed35b2049e385e0b7a0a62595368dc033cbb084b))
171
+ - **exodex:** retrieve gas price data from fee monitor ([#2129](https://github.com/ExodusMovement/exodus-hydra/issues/2129)) ([c878109](https://github.com/ExodusMovement/exodus-hydra/commit/c8781091c24c58fe45855ab40864f063f9bec899))
172
+ - fetchGasLimit for evm tokens ([#3985](https://github.com/ExodusMovement/exodus-hydra/issues/3985)) ([b530478](https://github.com/ExodusMovement/exodus-hydra/commit/b5304784e56efa24c363c19c6157910376b6ae16))
173
+ - get ADA token params from assetId ([#3541](https://github.com/ExodusMovement/exodus-hydra/issues/3541)) ([3054f5f](https://github.com/ExodusMovement/exodus-hydra/commit/3054f5f10cd4d181400e04a48711d6b64c36bca5))
174
+ - make ADA custom-token Nile-only ([#3295](https://github.com/ExodusMovement/exodus-hydra/issues/3295)) ([b2917e7](https://github.com/ExodusMovement/exodus-hydra/commit/b2917e7dad1584d225d2c55977311c2b7074cde6))
175
+ - missing function in matic api object ([#1953](https://github.com/ExodusMovement/exodus-hydra/issues/1953)) ([19c1091](https://github.com/ExodusMovement/exodus-hydra/commit/19c1091cb44c584d20f03804fa45d3c842d955c0))
176
+ - **nfts:** fix sending cardano NFT ([#3370](https://github.com/ExodusMovement/exodus-hydra/issues/3370)) ([ef437f0](https://github.com/ExodusMovement/exodus-hydra/commit/ef437f056afc2427d4fe816c154846855ba7a098))
177
+ - pass whole asset to AssetIcon ([#528](https://github.com/ExodusMovement/exodus-hydra/issues/528)) ([9256484](https://github.com/ExodusMovement/exodus-hydra/commit/9256484a39409a98bdcef08775c805e0736bdb73))
178
+ - paxg 0.02% tax row when sending ([#3351](https://github.com/ExodusMovement/exodus-hydra/issues/3351)) ([00741a3](https://github.com/ExodusMovement/exodus-hydra/commit/00741a3eecbbf5fbb24cf4e0238206e2648e323f))
179
+ - per token feature flag for balances ([#1753](https://github.com/ExodusMovement/exodus-hydra/issues/1753)) ([1524bc5](https://github.com/ExodusMovement/exodus-hydra/commit/1524bc5f30f6f68b52e9eb159c994e29783108ea))
180
+ - preserve asset APIs shape ([#2387](https://github.com/ExodusMovement/exodus-hydra/issues/2387)) ([f0abd59](https://github.com/ExodusMovement/exodus-hydra/commit/f0abd59db35d737be5761ea4b61abdb1eb947c9d))
181
+ - remove ADA adaAmount field from utxos ([#4074](https://github.com/ExodusMovement/exodus-hydra/issues/4074)) ([c804d80](https://github.com/ExodusMovement/exodus-hydra/commit/c804d8020f9c878c048c8bfb05ddd341475b927d))
182
+ - resolve issue causing slow restore wallet ([#1845](https://github.com/ExodusMovement/exodus-hydra/issues/1845)) ([8c60356](https://github.com/ExodusMovement/exodus-hydra/commit/8c60356082385ae470a7225627873006029fa0cc))
183
+ - revert accidentally reverted commits (backport) ([#4948](https://github.com/ExodusMovement/exodus-hydra/issues/4948)) ([f1c8ef1](https://github.com/ExodusMovement/exodus-hydra/commit/f1c8ef1b28776449880575a1cd07e31d9b135959))
184
+ - Send Aurora Tokens ([#4841](https://github.com/ExodusMovement/exodus-hydra/issues/4841)) ([0803e99](https://github.com/ExodusMovement/exodus-hydra/commit/0803e998321c4da8c1f41e2ff783bb47f6a4fb2b))
185
+ - serialize cursor correctly ([#3749](https://github.com/ExodusMovement/exodus-hydra/issues/3749)) ([8956d93](https://github.com/ExodusMovement/exodus-hydra/commit/8956d93b72d72c5f310d7f52529de92e02443444))
186
+ - show cardano as staking ([#3995](https://github.com/ExodusMovement/exodus-hydra/issues/3995)) ([9144eba](https://github.com/ExodusMovement/exodus-hydra/commit/9144eba98ef7f14228564da6b71f9ff6384f822a))
187
+ - skip ethereum when checking if assets restored ([#720](https://github.com/ExodusMovement/exodus-hydra/issues/720)) ([3841c58](https://github.com/ExodusMovement/exodus-hydra/commit/3841c58d9b27b110ea4b2673670800b0c0cf3b44))
188
+ - unify eth monitor helper ([#1046](https://github.com/ExodusMovement/exodus-hydra/issues/1046)) ([40d94c0](https://github.com/ExodusMovement/exodus-hydra/commit/40d94c07836824a3e3f0fa4c59d28d8aa0e83d04))
189
+ - update getTokensBalance call ([#550](https://github.com/ExodusMovement/exodus-hydra/issues/550)) ([870476f](https://github.com/ExodusMovement/exodus-hydra/commit/870476f95b78a2ae389116e7587d82a5af22836f))
190
+
191
+ ### Reverts
192
+
193
+ - enable arbitrum networks in prod ([#3733](https://github.com/ExodusMovement/exodus-hydra/issues/3733)) ([#3748](https://github.com/ExodusMovement/exodus-hydra/issues/3748)) ([44a9033](https://github.com/ExodusMovement/exodus-hydra/commit/44a903346d020103a86f841ef85ed3a339b2e1de))
194
+
195
+ ## [1.6.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@1.5.0...@exodus/assets-feature@1.6.0) (2023-09-07)
196
+
197
+ ### Features
198
+
199
+ - add asset preferences to assets-feature redux module ([#3879](https://github.com/ExodusMovement/exodus-hydra/issues/3879)) ([6b60a16](https://github.com/ExodusMovement/exodus-hydra/commit/6b60a162a2af845374911ccacf1c58dbf2620523))
200
+ - asset preferences api ([#3877](https://github.com/ExodusMovement/exodus-hydra/issues/3877)) ([5f1798b](https://github.com/ExodusMovement/exodus-hydra/commit/5f1798bc4ce616e4c762203b8f1940b898231aa1))
201
+
202
+ ### Bug Fixes
203
+
204
+ - asset-feature tests ([#3911](https://github.com/ExodusMovement/exodus-hydra/issues/3911)) ([c26d1ec](https://github.com/ExodusMovement/exodus-hydra/commit/c26d1ec7e0b32e1940c8dc6e969a5cb815661e20))
205
+
6
206
  ## [1.5.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/assets-feature@1.4.1...@exodus/assets-feature@1.5.0) (2023-08-30)
7
207
 
8
208
  ### Features
package/api/index.js ADDED
@@ -0,0 +1,19 @@
1
+ const createAssetsApi = ({ assetPreferences }) => {
2
+ return {
3
+ assetPreferences: {
4
+ enableMultiAddressMode: assetPreferences.enableMultiAddressMode,
5
+ disableMultiAddressMode: assetPreferences.disableMultiAddressMode,
6
+ disablePurpose: assetPreferences.disablePurpose,
7
+ enablePurpose: assetPreferences.enablePurpose,
8
+ },
9
+ }
10
+ }
11
+
12
+ const assetsApiDefinition = {
13
+ id: 'assetsApi',
14
+ type: 'api',
15
+ factory: createAssetsApi,
16
+ dependencies: ['assetPreferences'],
17
+ }
18
+
19
+ export default assetsApiDefinition
@@ -0,0 +1,18 @@
1
+ import { createStorageAtomFactory } from '@exodus/atoms'
2
+
3
+ const createDisabledPurposesAtom = ({ storage, logger, config }) => {
4
+ return createStorageAtomFactory({ storage, logger })({
5
+ key: 'disabledPurposes',
6
+ defaultValue: config.defaults ?? {},
7
+ isSoleWriter: true,
8
+ })
9
+ }
10
+
11
+ const disabledPurposesAtomDefinition = {
12
+ id: 'disabledPurposesAtom',
13
+ type: 'atom',
14
+ factory: createDisabledPurposesAtom,
15
+ dependencies: ['storage', 'logger', 'config'],
16
+ }
17
+
18
+ export default disabledPurposesAtomDefinition
@@ -0,0 +1,18 @@
1
+ import { createStorageAtomFactory } from '@exodus/atoms'
2
+
3
+ const createMultiAddressModeAtom = ({ storage, logger, config }) => {
4
+ return createStorageAtomFactory({ storage, logger })({
5
+ key: 'multiAddressMode',
6
+ defaultValue: config.defaults ?? {},
7
+ isSoleWriter: true,
8
+ })
9
+ }
10
+
11
+ const multiAddressModeAtomDefinition = {
12
+ id: 'multiAddressModeAtom',
13
+ type: 'atom',
14
+ factory: createMultiAddressModeAtom,
15
+ dependencies: ['storage', 'logger', 'config'],
16
+ }
17
+
18
+ export default multiAddressModeAtomDefinition
package/index.js CHANGED
@@ -1,12 +1,37 @@
1
1
  import assetsClientInterfaceDefinition from './client'
2
2
  import assetsPluginDefinition from './plugin'
3
+ import multiAddressModeAtomDefinition from './atoms/multi-address-mode'
4
+ import disabledPurposesAtomDefinition from './atoms/disabled-purposes'
5
+ import assetsApiDefinition from './api'
6
+ import assetModuleDefinition from './module'
7
+ import assetPreferencesDefinition from './module/asset-preferences'
3
8
 
4
- const assets = () => {
9
+ const assets = ({ config = {} } = {}) => {
5
10
  return {
6
11
  id: 'assets',
7
12
  definitions: [
8
13
  { definition: assetsClientInterfaceDefinition },
9
14
  { definition: assetsPluginDefinition },
15
+ {
16
+ definition: multiAddressModeAtomDefinition,
17
+ storage: { namespace: 'assetPreferences' },
18
+ config: config.multiAddressMode || {},
19
+ },
20
+ {
21
+ definition: disabledPurposesAtomDefinition,
22
+ storage: { namespace: 'assetPreferences' },
23
+ config: config.disabledPurposes || {},
24
+ },
25
+ { definition: assetsApiDefinition },
26
+ {
27
+ definition: assetModuleDefinition,
28
+ storage: { namespace: 'customTokens' },
29
+ aliases: [{ implementationId: 'unsafeStorage', interfaceId: 'storage' }],
30
+ },
31
+ {
32
+ definition: assetPreferencesDefinition,
33
+ writesAtoms: ['multiAddressModeAtom', 'disabledPurposesAtom'],
34
+ },
10
35
  ],
11
36
  }
12
37
  }
@@ -0,0 +1,61 @@
1
+ import ExodusModule from '@exodus/module'
2
+ import { omit } from '@exodus/basic-utils'
3
+
4
+ const MODULE_ID = 'assetPreferences'
5
+
6
+ class AssetPreferences extends ExodusModule {
7
+ #disabledPurposesAtom
8
+ #multiAddressModeAtom
9
+
10
+ constructor({ disabledPurposesAtom, multiAddressModeAtom, logger }) {
11
+ super({ name: MODULE_ID, logger })
12
+
13
+ this.#disabledPurposesAtom = disabledPurposesAtom
14
+ this.#multiAddressModeAtom = multiAddressModeAtom
15
+ }
16
+
17
+ enableMultiAddressMode = ({ assetNames }) =>
18
+ this.#multiAddressModeAtom.set((value) => ({
19
+ ...value,
20
+ ...assetNames.reduce((acc, assetName) => {
21
+ acc[assetName] = true
22
+ return acc
23
+ }, {}),
24
+ }))
25
+
26
+ disableMultiAddressMode = ({ assetNames }) =>
27
+ this.#multiAddressModeAtom.set((value) => omit(value, assetNames))
28
+
29
+ disablePurpose = ({ assetName, purpose }) =>
30
+ this.#disabledPurposesAtom.set((value) => {
31
+ const assetValue = value[assetName] || []
32
+ if (assetValue.includes(purpose)) return value
33
+
34
+ return {
35
+ ...value,
36
+ [assetName]: [...assetValue, purpose],
37
+ }
38
+ })
39
+
40
+ enablePurpose = ({ assetName, purpose }) =>
41
+ this.#disabledPurposesAtom.set((value) => {
42
+ const assetValue = value[assetName] || []
43
+ if (assetValue.includes(purpose)) {
44
+ return {
45
+ ...value,
46
+ [assetName]: assetValue.filter((disabledPurpose) => disabledPurpose !== purpose),
47
+ }
48
+ }
49
+
50
+ return value
51
+ })
52
+ }
53
+
54
+ const assetPreferencesDefinition = {
55
+ id: MODULE_ID,
56
+ type: 'module',
57
+ factory: (opts) => new AssetPreferences(opts),
58
+ dependencies: ['disabledPurposesAtom', 'multiAddressModeAtom', 'logger'],
59
+ }
60
+
61
+ export default assetPreferencesDefinition
@@ -0,0 +1,455 @@
1
+ /* eslint-disable unicorn/prefer-spread */
2
+ import {
3
+ createAssetRegistry,
4
+ CT_DEFAULT_SERVER,
5
+ CT_STATUS as STATUS,
6
+ CT_UPDATEABLE_PROPERTIES,
7
+ } from '@exodus/assets'
8
+ import { mapValues, pick, pickBy } from '@exodus/basic-utils'
9
+ // eslint-disable-next-line no-restricted-imports
10
+ import { fetchival } from '@exodus/fetch'
11
+ import ExodusModule from '@exodus/module'
12
+ // eslint-disable-next-line @exodus/restricted-imports/prefer-basic-utils
13
+ import { get, isEmpty, keyBy, once } from 'lodash'
14
+ import assert from 'minimalistic-assert'
15
+ import ms from 'ms'
16
+
17
+ import { CT_DATA_KEY, CT_TIMESTAMP_KEY, CT_UPDATE_INTERVAL } from './constants'
18
+ import { getAssetFromAssetId, getFetchErrorMessage, isDisabledCustomToken } from './utils'
19
+
20
+ const getFetchCacheKey = (baseAssetName, assetId) => `${assetId}-${baseAssetName}`
21
+
22
+ const _isDisabledCustomToken = (token) => token.lifecycleStatus === STATUS.DISABLED
23
+
24
+ const normalizeToken = (token) => (token.name ? token : { ...token, name: token.assetName })
25
+
26
+ export class AssetsModule extends ExodusModule {
27
+ #registry
28
+ #storage
29
+ #fetchCache
30
+ #config
31
+ #customTokensServerUrl
32
+ #updateableProps
33
+ #iconsStorage
34
+ #validateCustomToken
35
+ #assetPlugins
36
+ #combinedAssetsList
37
+ #storageDataKey
38
+ #storageTimestampKey
39
+ #customTokenUpdateInterval
40
+ #globalAssetRegistry // temporary
41
+
42
+ constructor({
43
+ storage,
44
+ iconsStorage,
45
+ assetPlugins,
46
+ combinedAssetsList,
47
+ globalAssetRegistry, // temporary
48
+ validateCustomToken = () => true,
49
+ config = {},
50
+ }) {
51
+ super({ name: 'AssetsModule' })
52
+ this.#storage = storage
53
+ this.#registry = {
54
+ getAsset: (assetName) => {
55
+ try {
56
+ throw new Error(`accessing asset ${assetName} too early`)
57
+ } catch (e) {
58
+ console.warn(e.message, e.stack)
59
+ }
60
+ },
61
+ getAssets: () => {
62
+ try {
63
+ throw new Error(`accessing assets too early`)
64
+ } catch (e) {
65
+ console.warn(e.message, e.stack)
66
+ }
67
+
68
+ return {}
69
+ },
70
+ }
71
+ this.#assetPlugins = assetPlugins
72
+ this.#combinedAssetsList = combinedAssetsList
73
+ this.#fetchCache = {}
74
+ this.#config = config
75
+ this.#customTokensServerUrl = config.customTokensServerUrl || CT_DEFAULT_SERVER
76
+ this.#updateableProps = [...CT_UPDATEABLE_PROPERTIES, 'version']
77
+ this.#iconsStorage = iconsStorage
78
+ this.#validateCustomToken = validateCustomToken
79
+ this.#storageDataKey = config.storageDataKey || CT_DATA_KEY
80
+ this.#storageTimestampKey = config.storageTimestampKey || CT_TIMESTAMP_KEY
81
+ this.#customTokenUpdateInterval = config.customTokenUpdateInterval || CT_UPDATE_INTERVAL
82
+ this.#globalAssetRegistry = globalAssetRegistry // temporary
83
+ }
84
+
85
+ // Assets Registry API
86
+
87
+ initialize = ({ assetClientInterface }) => {
88
+ // TODO: pass asset specific config to assets module in `config.assetsConfig`
89
+ const { assetsConfig = {} } = this.#config
90
+ const assetsList = Object.entries(this.#assetPlugins)
91
+ .map(([name, assetPlugin]) => {
92
+ const asset = assetPlugin.createAsset({ assetClientInterface, config: assetsConfig[name] })
93
+ if (asset.name === name) return asset
94
+ console.warn(`Incorrectly referenced supported asset ${name}. Expected ${asset.name}.`)
95
+ })
96
+ .filter(Boolean)
97
+ this.#registry = createAssetRegistry({
98
+ supportedAssetsList: [...assetsList, ...this.#combinedAssetsList],
99
+ globalAssetRegistry: this.#globalAssetRegistry, // temporarily supply the deprecated globalAssetRegistry until @exodus/models v10. Afterwards it can simply be removed.
100
+ })
101
+ // this.emit('assets-initialized')
102
+ }
103
+
104
+ getAssets = () => this.#registry.getAssets()
105
+
106
+ getAsset = (assetName) => this.#registry.getAsset(assetName)
107
+
108
+ getTokenNames = (baseAssetName) => {
109
+ assert(
110
+ baseAssetName === this.getAsset(baseAssetName).baseAsset.name,
111
+ 'getTokenNames(): baseAssetName expected'
112
+ )
113
+ return this.#getAssetNamesBy(
114
+ (otherAsset) =>
115
+ otherAsset.baseAsset.name === baseAssetName && otherAsset.name !== baseAssetName
116
+ )
117
+ }
118
+
119
+ getBaseAssetNames = () => this.#getAssetNamesBy((asset) => asset.baseAsset.name === asset.name)
120
+
121
+ load = once(async () => {
122
+ let importedTokens = {}
123
+ try {
124
+ const storedTokens = await this.#readCustomTokens()
125
+ const tokens = pickBy(storedTokens, ({ baseAssetName }) => {
126
+ const baseAsset = this.getAsset(baseAssetName)
127
+ return baseAsset.api?.hasFeature?.('customTokens')
128
+ })
129
+
130
+ // tokens may be:
131
+ // 1. pure custom tokens from the CTR
132
+ // 2. built-in tokens previously from the CTR
133
+ // 3. both of the above from a network that has feature flag customTokens === false
134
+ // It is OK to add all of these to the assets registry
135
+ const { added, updated } = this.#handleFetchedTokens(Object.values(tokens))
136
+ importedTokens = keyBy(added.concat(updated), 'name')
137
+ } catch (e) {
138
+ this._logger.log('error reading custom tokens from storage:', e)
139
+ }
140
+
141
+ this.emit('assets-load', { importedTokens })
142
+ })
143
+
144
+ // Custom tokens API
145
+
146
+ fetchToken = async (assetId, baseAssetName) => {
147
+ this.#assertSupportsCustomTokens(baseAssetName)
148
+
149
+ const asset = this.#getAssetFromAssetId(assetId, baseAssetName)
150
+ if (asset) {
151
+ // either built-in asset (isBuiltIn === true) or previously added custom token (isCustomToken === true)
152
+ return {
153
+ ...pick(asset, [
154
+ 'assetId',
155
+ 'baseAssetName',
156
+ 'displayName',
157
+ 'displayTicker',
158
+ 'gradientColors',
159
+ 'gradientCoords',
160
+ 'icon', // there is no icon field in built in assets, but there may be for custom tokens (TODO?)
161
+ 'isBuiltIn',
162
+ 'isCustomToken',
163
+ 'name',
164
+ 'primaryColor',
165
+ 'properName',
166
+ 'properTicker',
167
+ 'ticker',
168
+ ]),
169
+ assetName: asset.name,
170
+ }
171
+ }
172
+
173
+ const key = getFetchCacheKey(baseAssetName, assetId)
174
+ const cache = this.#getCache(key)
175
+ if (cache) return cache
176
+
177
+ const _token = await this.#fetch(`networks/${baseAssetName}`, { assetId }, 'token')
178
+ if (!this.#validateCustomToken(_token)) {
179
+ this._logger.warn('Invalid Custom Token schema')
180
+ throw new Error('Token did not pass validation')
181
+ }
182
+
183
+ const token = normalizeToken(_token)
184
+
185
+ try {
186
+ await this.#iconsStorage.storeIcons([token])
187
+ } catch (err) {
188
+ this._logger.warn(`An error occurred while decoding icon for ${assetId}`, err.message)
189
+ }
190
+
191
+ this.#setCache(key, token)
192
+ return this.#getCache(key)
193
+ }
194
+
195
+ addToken = async (assetId, baseAssetName) => {
196
+ this.#assertSupportsCustomTokens(baseAssetName)
197
+
198
+ try {
199
+ const token = await this.fetchToken(assetId, baseAssetName)
200
+ const { asset, isAdded } = this.#handleFetchedToken(token)
201
+
202
+ if (isAdded) {
203
+ await this.#storeCustomTokens([token])
204
+ this.#announceChanges({ added: [asset] })
205
+ } else {
206
+ this.#announceChanges({ updated: [asset] })
207
+ }
208
+
209
+ return asset
210
+ } catch (err) {
211
+ this._logger.warn(`Add custom token ${assetId} error on ${baseAssetName}`, err.message)
212
+ throw err
213
+ }
214
+ }
215
+
216
+ addTokens = async ({
217
+ assetIds,
218
+ baseAssetName,
219
+ allowedStatusList = [STATUS.VERIFIED, STATUS.CURATED, STATUS.UNVERIFIED],
220
+ }) => {
221
+ this.#assertSupportsCustomTokens(baseAssetName)
222
+
223
+ const tokens = await Promise.all(
224
+ assetIds.map(async (assetId) => {
225
+ try {
226
+ return await this.fetchToken(assetId, baseAssetName)
227
+ } catch (err) {
228
+ this._logger.warn('fetch custom tokens error:', err.message)
229
+ }
230
+ })
231
+ )
232
+ const filtered = tokens.filter((token) => {
233
+ if (!token) return false
234
+ const asset = this.getAsset(token.name)
235
+ return (
236
+ asset?.isBuiltIn ||
237
+ asset?.isCustomToken || // already present custom token
238
+ allowedStatusList.includes(token.lifecycleStatus) // new custom token from registry
239
+ )
240
+ })
241
+
242
+ if (filtered.length === 0) return []
243
+
244
+ const { fetchedAndHandledTokens, added, updated } = this.#handleFetchedTokens(filtered)
245
+
246
+ if (fetchedAndHandledTokens.length > 0) {
247
+ await this.#storeCustomTokens(fetchedAndHandledTokens)
248
+ }
249
+
250
+ this.#announceChanges({ added, updated })
251
+
252
+ return added.concat(updated)
253
+ }
254
+
255
+ addRemoteTokens = async ({ tokenNames }) => {
256
+ const assets = this.getAssets()
257
+ const tokenNamesToFetch = tokenNames.filter((tokenName) => !assets[tokenName])
258
+ if (tokenNamesToFetch.length === 0) return
259
+
260
+ const tokensToAdd = await this.#fetch('tokens', { tokenNames: tokenNamesToFetch }, 'tokens')
261
+ const tokensToUpdate = tokenNames
262
+ .map((tokenName) => assets[tokenName])
263
+ .filter((token) => !!token)
264
+ const tokens = [...tokensToAdd, ...tokensToUpdate]
265
+
266
+ if (isEmpty(tokens)) return
267
+
268
+ const validTokens = tokensToAdd.filter(this.#validateCustomToken).map(normalizeToken)
269
+ if (validTokens.length !== tokensToAdd.length) this._logger.warn('Invalid Custom Token schema')
270
+
271
+ const { fetchedAndHandledTokens, added, updated } = this.#handleFetchedTokens(tokens)
272
+
273
+ if (fetchedAndHandledTokens.length > 0) {
274
+ await this.#storeCustomTokens(fetchedAndHandledTokens)
275
+ await this.#iconsStorage.storeIcons(fetchedAndHandledTokens)
276
+ }
277
+
278
+ this.#announceChanges({ added, updated })
279
+ }
280
+
281
+ updateTokens = async () => {
282
+ const doUpdate = await this.#isNextUpdateDate()
283
+ if (!doUpdate) return
284
+
285
+ await this.#setLastUpdateDate() // update the timestamp in storage even if no updates are performed
286
+ const versionedAssetNames = await this.#getVersionedAssetNames()
287
+ const updatableAssetNames = versionedAssetNames.filter(({ assetName }) => {
288
+ const asset = this.getAsset(assetName)
289
+ return !asset?.isBuiltIn && asset?.baseAsset.api?.hasFeature?.('customTokens')
290
+ })
291
+ if (updatableAssetNames.length === 0) return
292
+
293
+ const tokens = await this.#fetchUpdates(updatableAssetNames)
294
+ if (tokens.length === 0) return
295
+
296
+ await this.#updateStoredCustomTokens(tokens)
297
+
298
+ // determine assets that are newly disabled before updating the #registry
299
+ const disabled = tokens.filter(
300
+ (token) => _isDisabledCustomToken(token) && !isDisabledCustomToken(this.getAsset(token.name))
301
+ )
302
+ const updatedAssets = tokens.map(this.#registry.updateCustomToken)
303
+ this.emit('assets-update', updatedAssets)
304
+ if (disabled.length > 0) {
305
+ this.emit('assets-disable', disabled)
306
+ }
307
+ }
308
+
309
+ searchTokens = async ({ baseAssetName, lifecycleStatus, query, excludeTags = ['offensive'] }) => {
310
+ baseAssetName && this.#assertSupportsCustomTokens(baseAssetName)
311
+
312
+ const tokens = await this.#fetch(
313
+ 'search',
314
+ { baseAssetName, lifecycleStatus, query, excludeTags },
315
+ 'tokens'
316
+ )
317
+ const validTokens = tokens.filter(this.#validateCustomToken).map(normalizeToken)
318
+
319
+ if (validTokens.length !== tokens.length) this._logger.warn('Invalid Custom Token schema')
320
+
321
+ return validTokens
322
+ }
323
+
324
+ #fetchUpdates = async (assetVersions) => {
325
+ const updatedTokens = await this.#fetch('updates', assetVersions, 'tokens')
326
+ const validatedTokens = updatedTokens.filter(this.#validateCustomToken).map(normalizeToken)
327
+ if (validatedTokens.length !== updatedTokens.length) {
328
+ this._logger.warn('Invalid Custom Token schema')
329
+ }
330
+
331
+ await this.#iconsStorage.storeIcons(validatedTokens)
332
+ return validatedTokens
333
+ }
334
+
335
+ #getAssetFromAssetId = (assetId, baseAssetName) =>
336
+ getAssetFromAssetId(this.getAssets(), assetId, baseAssetName)
337
+
338
+ #getAssetNamesBy = (filter) =>
339
+ Object.keys(this.getAssets()).filter((assetName) => filter(this.getAsset(assetName)))
340
+
341
+ #setCache = (key, value, ttl = ms('5m')) => {
342
+ this.#fetchCache[key] = { value, expiry: Date.now() + ttl }
343
+ }
344
+
345
+ #getCache = (key) => {
346
+ const item = this.#fetchCache[key]
347
+ return item?.value && Date.now() < item.expiry ? item.value : null
348
+ }
349
+
350
+ #announceChanges = ({ added = [], updated = [] }) => {
351
+ if (added.length > 0) this.emit('assets-add', added)
352
+ if (updated.length > 0) this.emit('assets-update', updated)
353
+ }
354
+
355
+ #fetch = async (endpoint, body, resultPath) => {
356
+ try {
357
+ const testDataParam = this.#config.fetchTestValues ? '?test=true' : ''
358
+ const res = await fetchival(this.#customTokensServerUrl)(`${endpoint}${testDataParam}`).post(
359
+ body
360
+ )
361
+
362
+ if (res.status !== 'OK')
363
+ throw new Error(`Custom tokens registry ${endpoint} ${res.status} ${res.message}`)
364
+
365
+ return get(res, resultPath)
366
+ } catch (error) {
367
+ throw new Error(await getFetchErrorMessage(error))
368
+ }
369
+ }
370
+
371
+ #handleFetchedToken = (token) => {
372
+ const asset = this.getAsset(token.name)
373
+ if (asset) return { asset, isAdded: false }
374
+
375
+ const { name } = this.#registry.addCustomToken(token) // add to `#/assets`
376
+ this._logger.log('Custom token added:', name)
377
+ return { asset: this.getAsset(name), isAdded: true }
378
+ }
379
+
380
+ #handleFetchedTokens = (tokens) => {
381
+ const fetchedAndHandledTokens = []
382
+ const added = []
383
+ const updated = []
384
+ for (const token of tokens) {
385
+ try {
386
+ const { asset, isAdded } = this.#handleFetchedToken(token)
387
+ if (isAdded) {
388
+ added.push(asset)
389
+ fetchedAndHandledTokens.push(token)
390
+ } else {
391
+ updated.push(asset)
392
+ }
393
+ } catch (err) {
394
+ this._logger.warn('Handle fetched custom tokens error:', err.message)
395
+ }
396
+ }
397
+
398
+ return { fetchedAndHandledTokens, added, updated }
399
+ }
400
+
401
+ #assertSupportsCustomTokens = (baseAssetName) => {
402
+ const baseAsset = this.getAsset(baseAssetName)
403
+ if (!baseAsset.api.hasFeature('customTokens')) {
404
+ const reason = `BUG: network ${baseAsset.name} does not support custom tokens.`
405
+ this._logger.warn(reason)
406
+ throw new Error(reason)
407
+ }
408
+ }
409
+
410
+ // Custom Tokens storage
411
+
412
+ #readCustomTokens = async () => {
413
+ const tokens = await this.#storage.get(this.#storageDataKey)
414
+ return mapValues(tokens, normalizeToken)
415
+ }
416
+
417
+ #storeCustomTokens = async (tokens) => {
418
+ const _tokens = await this.#readCustomTokens()
419
+ await this.#storage.set(this.#storageDataKey, { ..._tokens, ...keyBy(tokens, 'name') })
420
+ }
421
+
422
+ #updateStoredCustomTokens = async (tokens) => {
423
+ const _tokens = await this.#readCustomTokens()
424
+ // safer not to overwrite everything
425
+ const updates = tokens.map((token) => ({
426
+ ..._tokens[token.name],
427
+ ...pick(token, this.#updateableProps), // like state updates, safer not to overwrite everything in storage
428
+ }))
429
+ await this.#storage.set(this.#storageDataKey, { ..._tokens, ...keyBy(updates, 'name') })
430
+ }
431
+
432
+ #getVersionedAssetNames = async () => {
433
+ const tokens = await this.#readCustomTokens()
434
+ const filtered = Object.values(tokens).filter(({ name }) => !this.getAsset(name)?.isBuiltIn)
435
+ return filtered.map(({ assetName, version }) => ({ assetName, version }))
436
+ }
437
+
438
+ #setLastUpdateDate = async () => {
439
+ await this.#storage.set(this.#storageTimestampKey, Date.now())
440
+ }
441
+
442
+ #isNextUpdateDate = async () => {
443
+ const lastUpdateDate = await this.#storage.get(this.#storageTimestampKey)
444
+ return !lastUpdateDate || Date.now() - lastUpdateDate > this.#customTokenUpdateInterval
445
+ }
446
+
447
+ clear = () =>
448
+ Promise.all([
449
+ // should we also delete icons?
450
+ this.#storage.delete(this.#storageDataKey),
451
+ this.#storage.delete(this.#storageTimestampKey),
452
+ ])
453
+ }
454
+
455
+ export default AssetsModule
@@ -0,0 +1,5 @@
1
+ import ms from 'ms'
2
+
3
+ export const CT_DATA_KEY = 'customTokens'
4
+ export const CT_TIMESTAMP_KEY = 'customTokensLastUpdate'
5
+ export const CT_UPDATE_INTERVAL = ms('8h')
package/module/index.js CHANGED
@@ -1,5 +1,28 @@
1
- // TODO: ship assets module here in the future
1
+ // eslint-disable-next-line @exodus/restricted-imports/no-exodus-assets
2
+ import globalAssetRegistry from '@exodus/assets' // this is deprecated, do not use for any other purpose. Remove when we have @exodus/models v10.
3
+ import AssetsModule from './assets-module'
2
4
 
3
- const assetsModuleDefinition = {}
5
+ const createAssetsModule = ({
6
+ storage,
7
+ iconsStorage,
8
+ assetPlugins,
9
+ combinedAssetsList = [],
10
+ config,
11
+ }) =>
12
+ new AssetsModule({
13
+ storage,
14
+ iconsStorage,
15
+ globalAssetRegistry,
16
+ assetPlugins,
17
+ combinedAssetsList,
18
+ config,
19
+ })
20
+
21
+ const assetsModuleDefinition = {
22
+ id: 'assetsModule',
23
+ type: 'module',
24
+ factory: createAssetsModule,
25
+ dependencies: ['storage', 'iconsStorage', 'assetPlugins', 'combinedAssetsList?', 'config'],
26
+ }
4
27
 
5
28
  export default assetsModuleDefinition
@@ -0,0 +1,41 @@
1
+ import { CT_STATUS as STATUS } from '@exodus/assets'
2
+ import { get } from 'lodash'
3
+
4
+ const BLACKLISTED_ERRORS = [/Cannot destructure property 'owner'/]
5
+
6
+ export const getAssetFromAssetId = (assets, assetId, baseAssetName) => {
7
+ // ignore letter-case for assets that support checksummed addresses
8
+ assetId = assetId?.toLowerCase()
9
+ return Object.values(assets).find(
10
+ (asset) => assetId === asset.assetId?.toLowerCase() && baseAssetName === asset.baseAsset.name
11
+ )
12
+ }
13
+
14
+ export const isDisabledCustomToken = (asset) =>
15
+ asset.isCustomToken && asset.lifecycleStatus === STATUS.DISABLED
16
+
17
+ export async function getFetchErrorMessage(error) {
18
+ const defaultExtraMessage = [
19
+ error.message || 'Unknown',
20
+ error.response?.status,
21
+ error.response?.statusText,
22
+ ]
23
+ .filter(Boolean)
24
+ .join(' - ')
25
+
26
+ try {
27
+ // Note. I'm not returning responseBody directly because it could be a large error (html) response when the server is down.
28
+ const responseBody = await error.response?.text?.()
29
+
30
+ if (!responseBody) return defaultExtraMessage
31
+
32
+ const message = get(JSON.parse(responseBody), 'message')
33
+ const isBlacklisted = BLACKLISTED_ERRORS.some((v) => v.test(message))
34
+
35
+ if (isBlacklisted) return 'Could not fetch token'
36
+
37
+ return message || defaultExtraMessage
38
+ } catch {
39
+ return defaultExtraMessage
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/assets-feature",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Assets module, clients and apis",
6
6
  "main": "index.js",
@@ -15,6 +15,8 @@
15
15
  "module",
16
16
  "plugin",
17
17
  "redux",
18
+ "api",
19
+ "atoms",
18
20
  "README.md",
19
21
  "CHANGELOG.md",
20
22
  "!**/__tests__/**"
@@ -29,16 +31,26 @@
29
31
  },
30
32
  "dependencies": {
31
33
  "@exodus/basic-utils": "^2.1.0",
34
+ "@exodus/bitcoin-plugin": "^1.0.3",
35
+ "@exodus/bitcoinregtest-plugin": "^1.0.3",
36
+ "@exodus/bitcointestnet-plugin": "^1.0.3",
37
+ "@exodus/combined-assets-meta": "^1.2.5",
38
+ "@exodus/cosmos-plugin": "^1.0.0",
39
+ "@exodus/fetch": "^1.2.1",
40
+ "@exodus/osmosis-plugin": "^1.0.0",
32
41
  "lodash": "^4.17.21",
33
42
  "minimalistic-assert": "^1.0.1",
43
+ "ms": "^2.1.3",
34
44
  "reselect": "^3.0.1"
35
45
  },
36
46
  "devDependencies": {
37
- "@exodus/assets": "^8.0.94",
47
+ "@exodus/assets": "^8.0.95",
48
+ "@exodus/atoms": "^5.7.0",
38
49
  "@exodus/available-assets": "^5.1.1",
39
50
  "@exodus/bitcoin-meta": "^1.0.1",
40
51
  "@exodus/ethereum-meta": "^1.0.30",
41
52
  "@exodus/models": "^9.1.1",
53
+ "@exodus/module": "^1.2.0",
42
54
  "@exodus/redux-dependency-injection": "^3.0.0",
43
55
  "@exodus/storage-memory": "^2.1.1",
44
56
  "@exodus/wallet-accounts": "^14.0.0",
@@ -48,5 +60,5 @@
48
60
  "jest": "^29.1.2",
49
61
  "redux": "^4.0.0"
50
62
  },
51
- "gitHead": "13c8d20b56723fe9556683c3ff969995556ba5f2"
63
+ "gitHead": "e1dc646af5bdede1eeadb95b548c173aceddabcd"
52
64
  }
package/plugin/index.js CHANGED
@@ -1,4 +1,6 @@
1
- const createAssetsPlugin = ({ port, assetsModule }) => {
1
+ import { createAtomObserver } from '@exodus/atoms'
2
+
3
+ const createAssetsPlugin = ({ port, assetsModule, disabledPurposesAtom, multiAddressModeAtom }) => {
2
4
  const emitAssetsLoad = () => port.emit('assets-load', Object.values(assetsModule.getAssets()))
3
5
  const listeners = [
4
6
  ['assets-add', (data) => port.emit('assets-add', data)],
@@ -9,12 +11,21 @@ const createAssetsPlugin = ({ port, assetsModule }) => {
9
11
  assetsModule.on(event, callback)
10
12
  })
11
13
 
14
+ const observers = [
15
+ createAtomObserver({ atom: disabledPurposesAtom, port, event: 'disabledPurposes' }),
16
+ createAtomObserver({ atom: multiAddressModeAtom, port, event: 'multiAddressMode' }),
17
+ ]
18
+
12
19
  const onStart = async () => {
20
+ observers.forEach((observer) => observer.register())
21
+
13
22
  await assetsModule.load()
14
23
  emitAssetsLoad()
15
24
  }
16
25
 
17
26
  const onLoad = async () => {
27
+ observers.forEach((observer) => observer.start())
28
+
18
29
  await assetsModule.load()
19
30
  emitAssetsLoad()
20
31
  }
@@ -23,6 +34,8 @@ const createAssetsPlugin = ({ port, assetsModule }) => {
23
34
  listeners.forEach(([event, callback]) => {
24
35
  assetsModule.removeListener(event, callback)
25
36
  })
37
+
38
+ observers.forEach((observer) => observer.unregister())
26
39
  }
27
40
 
28
41
  return { onStart, onLoad, onStop }
@@ -32,7 +45,7 @@ const assetsPluginDefinition = {
32
45
  id: 'assetsPlugin',
33
46
  type: 'plugin',
34
47
  factory: createAssetsPlugin,
35
- dependencies: ['port', 'assetsModule'],
48
+ dependencies: ['port', 'assetsModule', 'disabledPurposesAtom', 'multiAddressModeAtom'],
36
49
  }
37
50
 
38
51
  export default assetsPluginDefinition
package/redux/index.js CHANGED
@@ -17,12 +17,21 @@ const assetsReduxDefinition = {
17
17
  initialState,
18
18
  eventReducers: {
19
19
  'assets-load': (state, payload) => ({
20
+ ...state,
20
21
  error: null,
21
22
  loaded: true,
22
23
  data: keyBy(payload, 'name'),
23
24
  }),
24
25
  'assets-add': mergeAssets,
25
26
  'assets-update': mergeAssets,
27
+ multiAddressMode: (state, payload) => ({
28
+ ...state,
29
+ multiAddressMode: payload,
30
+ }),
31
+ disabledPurposes: (state, payload) => ({
32
+ ...state,
33
+ disabledPurposes: payload,
34
+ }),
26
35
  },
27
36
  selectorDefinitions,
28
37
  }
@@ -2,6 +2,9 @@ const initialState = {
2
2
  loaded: false,
3
3
  data: {},
4
4
  error: null,
5
+ // asset preferences
6
+ multiAddressMode: {},
7
+ disabledPurposes: {},
5
8
  }
6
9
 
7
10
  export default initialState
@@ -49,6 +49,34 @@ const getAssetFromTickerSelectorDefinition = {
49
49
  dependencies: [{ selector: 'allByTicker' }],
50
50
  }
51
51
 
52
+ const createMultiAddressModeSelectorDefinition = {
53
+ id: 'createMultiAddressMode',
54
+ selectorFactory: (multiAddressModeDataSelector) =>
55
+ memoize((assetName) =>
56
+ createSelector(
57
+ multiAddressModeDataSelector,
58
+ (multiAddressModeData) => multiAddressModeData[assetName] || false
59
+ )
60
+ ),
61
+
62
+ dependencies: [{ selector: 'multiAddressMode' }],
63
+ }
64
+
65
+ const createDisabledPurposesSelectorDefinition = {
66
+ id: 'createDisabledPurposes',
67
+ selectorFactory: (disabledPurposesDataSelector) => {
68
+ const disabledPurposesDefaultValue = []
69
+ return memoize((assetName) =>
70
+ createSelector(
71
+ disabledPurposesDataSelector,
72
+ (disabledPurposesData) => disabledPurposesData[assetName] || disabledPurposesDefaultValue
73
+ )
74
+ )
75
+ },
76
+
77
+ dependencies: [{ selector: 'disabledPurposes' }],
78
+ }
79
+
52
80
  const assetSelectors = [
53
81
  allAssetsSelectorDefinition,
54
82
  allByTickerSelectorDefinition,
@@ -57,6 +85,8 @@ const assetSelectors = [
57
85
  createFeeAssetSelectorDefinition,
58
86
  getAssetSelectorDefinition,
59
87
  getAssetFromTickerSelectorDefinition,
88
+ createMultiAddressModeSelectorDefinition,
89
+ createDisabledPurposesSelectorDefinition,
60
90
  ]
61
91
 
62
92
  export default assetSelectors