@exodus/headless 2.0.0-alpha.41 → 2.0.0-alpha.43

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,26 @@
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
+ ## [2.0.0-alpha.43](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/headless@2.0.0-alpha.42...@exodus/headless@2.0.0-alpha.43) (2023-06-16)
7
+
8
+ ### Features
9
+
10
+ - **headless:** integrate ab-testing module ([#1972](https://github.com/ExodusMovement/exodus-hydra/issues/1972)) ([6c063b4](https://github.com/ExodusMovement/exodus-hydra/commit/6c063b4d4f04d08c36f843577822e7d53f002d4f))
11
+
12
+ ## [2.0.0-alpha.42](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/headless@2.0.0-alpha.41...@exodus/headless@2.0.0-alpha.42) (2023-06-15)
13
+
14
+ ### Features
15
+
16
+ - .use poc using with kyc and referrals ([#1778](https://github.com/ExodusMovement/exodus-hydra/issues/1778)) ([7f66163](https://github.com/ExodusMovement/exodus-hydra/commit/7f66163364517fe555de220ad3cafd86c7c68516))
17
+ - **headless:** use(nfts) ([#1963](https://github.com/ExodusMovement/exodus-hydra/issues/1963)) ([47ed141](https://github.com/ExodusMovement/exodus-hydra/commit/47ed141b93291938d8790a8697b55c9c9af230bd))
18
+ - use() more modules ([#1953](https://github.com/ExodusMovement/exodus-hydra/issues/1953)) ([4f0d987](https://github.com/ExodusMovement/exodus-hydra/commit/4f0d987e2b3c01829e11b430988168754ebce8f6))
19
+
20
+ ### Bug Fixes
21
+
22
+ - new use modules api export ([#1958](https://github.com/ExodusMovement/exodus-hydra/issues/1958)) ([c7e87eb](https://github.com/ExodusMovement/exodus-hydra/commit/c7e87eb01f5fb79fcc3454a11485dadf638f88ef))
23
+ - remove duplicated kyc api ([#1955](https://github.com/ExodusMovement/exodus-hydra/issues/1955)) ([3357590](https://github.com/ExodusMovement/exodus-hydra/commit/33575908517230b5a187f0ac3b6e32e75982abdb))
24
+ - require plugin methods to be declared correctly ([#1945](https://github.com/ExodusMovement/exodus-hydra/issues/1945)) ([e7feae6](https://github.com/ExodusMovement/exodus-hydra/commit/e7feae6cfaa3cd0e9f9d074f4b5306a264d16b74))
25
+
6
26
  ## [2.0.0-alpha.41](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/headless@2.0.0-alpha.40...@exodus/headless@2.0.0-alpha.41) (2023-06-15)
7
27
 
8
28
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/headless",
3
- "version": "2.0.0-alpha.41",
3
+ "version": "2.0.0-alpha.43",
4
4
  "description": "The headless Exodus wallet SDK",
5
5
  "author": "Exodus Movement Inc",
6
6
  "main": "src/index.js",
@@ -26,6 +26,7 @@
26
26
  "test": "jest --runInBand"
27
27
  },
28
28
  "dependencies": {
29
+ "@exodus/ab-testing": "^5.1.0",
29
30
  "@exodus/address-provider": "^4.0.0",
30
31
  "@exodus/apy-rates": "^2.1.0",
31
32
  "@exodus/atoms": "^4.0.1",
@@ -87,5 +88,5 @@
87
88
  "nock": "^13.3.1",
88
89
  "p-defer": "^4.0.0"
89
90
  },
90
- "gitHead": "c548938b8c6dee498023373e2122cb95da1005f5"
91
+ "gitHead": "67ed7c439d9d3626db03d8c926af3e1fbe19cf8f"
91
92
  }
package/src/api.js CHANGED
@@ -1,45 +1,34 @@
1
1
  import { validateMnemonic as isMnemonicValid } from 'bip39'
2
2
 
3
3
  const createApi = ({ ioc, port }) => {
4
+ const apis = ioc.getByType('api')
5
+
4
6
  const { assetsModule } = ioc.getByType('adapter')
5
7
 
6
8
  const {
7
9
  addressProvider,
8
10
  application,
9
11
  blockchainMetadata,
10
- connectedOrigins,
11
12
  enabledAssets,
12
- kyc,
13
- nfts,
14
13
  personalNotes,
15
- referrals,
16
14
  remoteConfig,
17
15
  wallet,
18
- walletAccounts,
19
16
  } = ioc.getByType('module')
20
17
 
21
18
  const { feeMonitors, ratesMonitor, nftsMonitor } = ioc.getByType('monitor')
19
+
22
20
  // TODO: decide where this belongs
23
21
  const { passphraseCache } = ioc.getAll()
24
22
 
25
- const {
26
- // ...
27
- currencyAtom,
28
- enabledWalletAccountsAtom,
29
- languageAtom,
30
- connectedOriginsAtom,
31
- } = ioc.getByType('atom')
32
-
33
23
  // TODO: do this on 'unload'
34
24
  const stop = () => {
35
- kyc.stop()
36
- referrals.stop()
37
25
  remoteConfig.stop()
38
26
  feeMonitors.stop()
39
- nftsMonitor.stop()
27
+ nftsMonitor?.stop()
40
28
  }
41
29
 
42
30
  return {
31
+ ...Object.assign({}, ...Object.values(apis)),
43
32
  wallet: {
44
33
  exists: () => wallet.exists(),
45
34
  start: application.start,
@@ -62,13 +51,6 @@ const createApi = ({ ioc, port }) => {
62
51
  changeLockTimer: application.changeLockTimer,
63
52
  isLocked: () => wallet.isLocked(),
64
53
  },
65
- walletAccounts: {
66
- create: walletAccounts.create,
67
- update: walletAccounts.update,
68
- disable: walletAccounts.disable,
69
- enable: walletAccounts.enable,
70
- getEnabled: enabledWalletAccountsAtom.get,
71
- },
72
54
  blockchainMetadata: {
73
55
  getTxLog: blockchainMetadata.getTxLog,
74
56
  getLoadedTxLogs: blockchainMetadata.getLoadedTxLogs,
@@ -95,10 +77,6 @@ const createApi = ({ ioc, port }) => {
95
77
  get: remoteConfig.get,
96
78
  getAll: remoteConfig.getAll,
97
79
  },
98
- locale: {
99
- setLanguage: (value) => languageAtom.set(value),
100
- setCurrency: (value) => currencyAtom.set(value),
101
- },
102
80
  rates: {
103
81
  refresh: () => ratesMonitor.update(),
104
82
  },
@@ -107,36 +85,9 @@ const createApi = ({ ioc, port }) => {
107
85
  getSupportedPurposes: addressProvider.getSupportedPurposes.bind(addressProvider),
108
86
  getReceiveAddress: addressProvider.getReceiveAddress.bind(addressProvider),
109
87
  },
110
- kyc: {
111
- start: kyc.start,
112
- sync: kyc.sync,
113
- requestKycToken: kyc.requestKycToken,
114
- },
115
- referrals: {
116
- setReferredBy: referrals.setReferredBy,
117
- referralCodeExists: referrals.referralCodeExists,
118
- },
119
88
  personalNotes: {
120
89
  upsert: personalNotes.upsert,
121
90
  },
122
- nfts: {
123
- upsertConfig: nfts.upsertConfig,
124
- setMonitorInterval: nftsMonitor.setInterval,
125
- },
126
- connectedOrigins: {
127
- get: connectedOriginsAtom.get,
128
- add: connectedOrigins.add,
129
- clear: connectedOrigins.clear,
130
- untrust: connectedOrigins.untrust,
131
- isTrusted: connectedOrigins.isTrusted,
132
- isAutoApprove: connectedOrigins.isAutoApprove,
133
- setFavorite: connectedOrigins.setFavorite,
134
- setAutoApprove: connectedOrigins.setAutoApprove,
135
- connect: connectedOrigins.connect,
136
- disconnect: connectedOrigins.disconnect,
137
- updateConnection: connectedOrigins.updateConnection,
138
- clearConnections: connectedOrigins.clearConnections,
139
- },
140
91
  isMnemonicValid,
141
92
  subscribe: port.subscribe.bind(port),
142
93
  unsubscribe: port.unsubscribe.bind(port),
@@ -3,6 +3,8 @@
3
3
  import ExodusModule from '@exodus/module'
4
4
  import assert from 'minimalistic-assert'
5
5
 
6
+ import { LifecycleHook as Hook } from './constants'
7
+
6
8
  // Because we are forced to restart wallet after deleting/importing, we need to store certain flags to persist state between executions
7
9
 
8
10
  // Triggers deletetion logic when wallet starts.
@@ -17,23 +19,6 @@ const IMPORT_FLAG = 'importFlag'
17
19
  // Set as true on import method is called, and set to false after restore is completed
18
20
  const RESTORE_FLAG = 'restoreFlag'
19
21
 
20
- const Hook = Object.freeze({
21
- Lock: 'lock',
22
- Unlock: 'unlock',
23
- Clear: 'clear',
24
- Import: 'import',
25
- Migrate: 'migrate',
26
- Start: 'start',
27
- Load: 'load',
28
- Unload: 'unload',
29
- Create: 'create',
30
- Backup: 'backup',
31
- Restore: 'restore',
32
- RestoreCompleted: 'restore-completed',
33
- AssetsSynced: 'assets-synced',
34
- ChangePassphrase: 'change-passphrase',
35
- })
36
-
37
22
  const HOOKS = new Set(Object.values(Hook))
38
23
 
39
24
  class Application extends ExodusModule {
package/src/constants.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export const atomsToAttach = [
2
+ 'abTestingAtom',
2
3
  'apyRatesAtom',
3
4
  'availableAssetNamesAtom',
4
5
  'balancesAtom',
@@ -16,3 +17,20 @@ export const atomsToAttach = [
16
17
  'topMoversAtom',
17
18
  'walletAccountsAtom',
18
19
  ]
20
+
21
+ export const LifecycleHook = Object.freeze({
22
+ Lock: 'lock',
23
+ Unlock: 'unlock',
24
+ Clear: 'clear',
25
+ Import: 'import',
26
+ Migrate: 'migrate',
27
+ Start: 'start',
28
+ Load: 'load',
29
+ Unload: 'unload',
30
+ Create: 'create',
31
+ Backup: 'backup',
32
+ Restore: 'restore',
33
+ RestoreCompleted: 'restore-completed',
34
+ AssetsSynced: 'assets-synced',
35
+ ChangePassphrase: 'change-passphrase',
36
+ })
@@ -1,13 +1,11 @@
1
1
  import { apyRatesAtomDefinition } from '@exodus/apy-rates/atoms'
2
2
  import {
3
- createFusionAtomFactory,
4
3
  createInMemoryAtom,
5
4
  createRemoteConfigAtomFactory,
6
5
  createStorageAtomFactory,
7
6
  } from '@exodus/atoms'
8
7
  import { availableAssetNamesAtomDefinition } from '@exodus/available-assets/atoms'
9
8
  import { balancesAtomDefinition } from '@exodus/balances/atoms'
10
- import { connectedOriginsAtomDefinition } from '@exodus/connected-origins/atoms'
11
9
  import { cryptoNewsAtomDefinition } from '@exodus/crypto-news-monitor/atoms'
12
10
  import {
13
11
  enabledAndDisabledAssetsAtomDefinition,
@@ -15,16 +13,9 @@ import {
15
13
  } from '@exodus/enabled-assets/atoms'
16
14
  import { featureFlagsAtomDefinition } from '@exodus/feature-flags/atoms'
17
15
  import { geolocationAtomDefinition } from '@exodus/geolocation/atoms'
18
- import { kycAtomDefinition } from '@exodus/kyc/atoms'
19
- import { nftsCacheAtomDefinition, nftsConfigsAtomDefinition } from '@exodus/nfts/atoms'
20
16
  import { personalNotesAtomDefinition } from '@exodus/personal-notes/atoms'
21
17
  import { ratesAtomDefinition } from '@exodus/rates-monitor/atoms'
22
- import { referralsAtomDefinition } from '@exodus/referrals/atoms'
23
18
  import { topMoversAtomDefinition } from '@exodus/top-movers-monitor/atoms'
24
- import {
25
- enabledWalletAccountsAtomDefinition,
26
- walletAccountsAtomDefinition,
27
- } from '@exodus/wallet-accounts/atoms'
28
19
 
29
20
  import baseAssetNamesToMonitorAtomDefinition from '../atoms/base-asset-names-to-monitor'
30
21
  import nonDustBalanceAssetNamesAtomDefinition from '../atoms/non-dust-balance-asset-names-atom'
@@ -40,11 +31,6 @@ const createAtomDependencies = () =>
40
31
  dependencies: [],
41
32
  },
42
33
  },
43
- {
44
- definition: walletAccountsAtomDefinition,
45
- storage: { namespace: 'walletAccounts' },
46
- },
47
- { definition: enabledWalletAccountsAtomDefinition },
48
34
  {
49
35
  definition: enabledAndDisabledAssetsAtomDefinition,
50
36
  storage: { namespace: 'enabledAssets' },
@@ -65,36 +51,6 @@ const createAtomDependencies = () =>
65
51
  dependencies: ['config', 'remoteConfig'],
66
52
  },
67
53
  },
68
- {
69
- definition: {
70
- id: 'languageAtom',
71
- factory: ({ storage, config }) =>
72
- createStorageAtomFactory({ storage })({
73
- key: 'language',
74
- defaultValue: config.defaultValue,
75
- isSoleWriter: true,
76
- }),
77
- dependencies: ['storage', 'config'],
78
- },
79
- aliases: [
80
- {
81
- implementationId: 'unsafeStorage',
82
- interfaceId: 'storage',
83
- },
84
- ],
85
- storage: { namespace: 'locale' },
86
- },
87
- {
88
- definition: {
89
- id: 'currencyAtom',
90
- factory: ({ fusion, config }) =>
91
- createFusionAtomFactory({ fusion })({
92
- path: `private.currency`,
93
- defaultValue: config.defaultValue,
94
- }),
95
- dependencies: ['fusion', 'config'],
96
- },
97
- },
98
54
  // TODO: move to @exodus/market-history
99
55
  {
100
56
  definition: {
@@ -144,8 +100,6 @@ const createAtomDependencies = () =>
144
100
  { definition: ratesAtomDefinition },
145
101
  { definition: geolocationAtomDefinition },
146
102
  { definition: featureFlagsAtomDefinition },
147
- { definition: kycAtomDefinition },
148
- { definition: referralsAtomDefinition },
149
103
  {
150
104
  definition: personalNotesAtomDefinition,
151
105
  aliases: [
@@ -155,30 +109,11 @@ const createAtomDependencies = () =>
155
109
  },
156
110
  ],
157
111
  },
158
- {
159
- definition: connectedOriginsAtomDefinition,
160
- storage: { namespace: 'connectedOrigins' },
161
- aliases: [
162
- {
163
- implementationId: 'unsafeStorage',
164
- interfaceId: 'storage',
165
- },
166
- ],
167
- },
168
112
  { definition: baseAssetNamesToMonitorAtomDefinition },
169
113
  { definition: topMoversAtomDefinition },
170
114
  { definition: cryptoNewsAtomDefinition },
171
115
  { definition: restoreAtomDefinition },
172
116
  { definition: apyRatesAtomDefinition },
173
- {
174
- definition: nftsCacheAtomDefinition,
175
- storage: { namespace: 'nftsCache' },
176
- },
177
- {
178
- definition: nftsConfigsAtomDefinition,
179
- aliases: [{ implementationId: 'unsafeStorage', interfaceId: 'storage' }],
180
- storage: { namespace: 'nfts-config' },
181
- },
182
117
  ].map(withType('atom'))
183
118
 
184
119
  export default createAtomDependencies
@@ -3,18 +3,13 @@ import availableAssetsModuleDefinition from '@exodus/available-assets/module'
3
3
  import balancesDefinition from '@exodus/balances/module'
4
4
  import blockchainMetadataDefinition from '@exodus/blockchain-metadata/module'
5
5
  import createRemoteConfig from '@exodus/config/remote'
6
- import connectedOriginsDefinition from '@exodus/connected-origins/module'
7
6
  import enabledAssetsModuleDefinition from '@exodus/enabled-assets/module'
8
7
  import createExodusPricingClient from '@exodus/exodus-pricing-client'
9
8
  import featureFlagsDefinition from '@exodus/feature-flags/module'
10
9
  import createKeyIdentifierProvider from '@exodus/key-identifier-provider'
11
10
  import keychainDefinition from '@exodus/keychain/module'
12
- import kycDefinition from '@exodus/kyc/module'
13
- import nftsModuleDefinition from '@exodus/nfts/module'
14
11
  import personalNotesDefinition from '@exodus/personal-notes/module'
15
- import referralsDefinition from '@exodus/referrals/module'
16
12
  import walletDefinition from '@exodus/wallet/module'
17
- import walletAccountsDefinition from '@exodus/wallet-accounts/module'
18
13
  import walletCompatibilityModesDefinition from '@exodus/wallet-compatibility-modes/module'
19
14
  import EventEmitter from 'events/'
20
15
 
@@ -49,7 +44,6 @@ const createModuleDependencies = ({ config }) =>
49
44
  definition: walletCompatibilityModesDefinition,
50
45
  },
51
46
  { definition: unlockEncryptedStorageDefinition },
52
- { definition: walletAccountsDefinition, writesAtoms: ['walletAccountsAtom'] },
53
47
  {
54
48
  definition: blockchainMetadataDefinition,
55
49
  storage: { namespace: ['blockchain', 'v1'] },
@@ -79,31 +73,9 @@ const createModuleDependencies = ({ config }) =>
79
73
  writesAtoms: ['featureFlagAtoms'],
80
74
  },
81
75
  ...createAddressProviderDependencies(config.addressProvider),
82
- {
83
- definition: kycDefinition,
84
- writesAtoms: ['kycAtom'],
85
- },
86
- {
87
- definition: referralsDefinition,
88
- storage: { namespace: 'referrals' },
89
- writesAtoms: ['referralsAtom'],
90
- aliases: [
91
- {
92
- implementationId: 'unsafeStorage',
93
- interfaceId: 'storage',
94
- },
95
- ],
96
- },
97
76
  {
98
77
  definition: personalNotesDefinition,
99
78
  },
100
- {
101
- definition: connectedOriginsDefinition,
102
- writesAtoms: ['connectedOriginsAtom'],
103
- },
104
- {
105
- definition: nftsModuleDefinition,
106
- },
107
79
  ].map(withType('module'))
108
80
 
109
81
  export default createModuleDependencies
@@ -3,7 +3,6 @@ import cryptoNewsMonitorDefinition from '@exodus/crypto-news-monitor/monitor'
3
3
  import feeMonitorsDefinition from '@exodus/fee-monitors/monitor'
4
4
  import geolocationMonitorDefinition from '@exodus/geolocation/monitor'
5
5
  import marketHistoryMonitorDefinition from '@exodus/market-history/module'
6
- import nftsMonitorDefinition from '@exodus/nfts/monitor'
7
6
  import ratesMonitorDefinition from '@exodus/rates-monitor/module'
8
7
  import localTopMoversMonitorDefinition from '@exodus/top-movers-monitor/monitor/local'
9
8
  import remoteTopMoversMonitorDefinition from '@exodus/top-movers-monitor/monitor/remote'
@@ -57,10 +56,6 @@ const createMonitorDependencies = ({ config }) =>
57
56
  definition: apyRatesMonitorDefinition,
58
57
  writesAtoms: ['apyRatesAtom'],
59
58
  },
60
- {
61
- definition: nftsMonitorDefinition,
62
- writesAtoms: ['nftsCacheAtom'],
63
- },
64
59
  ].map(withType('monitor'))
65
60
 
66
61
  export default createMonitorDependencies
package/src/index.js CHANGED
@@ -4,6 +4,13 @@ import createApi from './api'
4
4
  import attachAtoms from './atoms/attach'
5
5
  import { atomsToAttach } from './constants'
6
6
  import createIOC from './ioc'
7
+ import abTesting from './modules/ab-testing'
8
+ import connectedOrigins from './modules/connected-origins'
9
+ import kyc from './modules/kyc'
10
+ import locale from './modules/locale'
11
+ import nfts from './modules/nfts'
12
+ import referrals from './modules/referrals'
13
+ import walletAccounts from './modules/wallet-accounts'
7
14
  import attachPlugins from './plugins/attach'
8
15
  import { createLoadWalletAccountsHandler } from './utils/blockchain-metadata'
9
16
 
@@ -11,26 +18,29 @@ const createExodus = ({ adapters, config, port }) => {
11
18
  const ioc = createIOC({ adapters, config })
12
19
  const { headless: headlessConfig } = config
13
20
 
21
+ ioc.use(walletAccounts())
22
+ ioc.use(locale())
23
+ ioc.use(nfts())
24
+ ioc.use(kyc())
25
+ ioc.use(referrals())
26
+ ioc.use(connectedOrigins())
27
+ ioc.use(abTesting())
28
+
29
+ ioc.register({ definition: { id: 'port', type: 'port', factory: () => port } })
30
+
14
31
  const resolve = () => {
15
32
  ioc.resolve()
16
33
 
17
34
  const { assetsModule, storage } = ioc.getByType('adapter')
18
35
 
19
- const { languageAtom } = ioc.getByType('atom')
20
-
21
36
  const {
22
37
  application,
23
38
  blockchainMetadata,
24
- connectedOrigins,
25
39
  enabledAssets,
26
40
  featureFlags,
27
- kyc,
28
- nfts,
29
41
  personalNotes,
30
- referrals,
31
42
  remoteConfig,
32
43
  unlockEncryptedStorage,
33
- walletAccounts,
34
44
  } = ioc.getByType('module')
35
45
 
36
46
  const {
@@ -39,7 +49,6 @@ const createExodus = ({ adapters, config, port }) => {
39
49
  feeMonitors,
40
50
  geolocationMonitor,
41
51
  marketHistory,
42
- nftsMonitor,
43
52
  ratesMonitor,
44
53
  topMoversMonitor,
45
54
  } = ioc.getByType('monitor')
@@ -71,9 +80,6 @@ const createExodus = ({ adapters, config, port }) => {
71
80
  // TODO: migrate to ratesAtom. Will be easier once it's on headless
72
81
  ratesMonitor.on('rates', (payload) => port.emit('rates', payload))
73
82
 
74
- nftsMonitor.on('nfts', (data) => port.emit('nfts', data))
75
- nftsMonitor.on('nfts-txs', (data) => port.emit('nfts-txs', data))
76
-
77
83
  application.hook('start', (payload) => {
78
84
  remoteConfig.load()
79
85
  featureFlags.load()
@@ -90,23 +96,14 @@ const createExodus = ({ adapters, config, port }) => {
90
96
  ratesMonitor.start()
91
97
  feeMonitors.start()
92
98
  apyRatesMonitor.start()
93
- nftsMonitor.start()
94
99
 
95
100
  await assetsModule.load()
96
- await connectedOrigins.load()
97
- await walletAccounts.load()
98
101
  await Promise.all([
99
102
  //
100
103
  blockchainMetadata.load(),
101
104
  enabledAssets.load(),
102
105
  ])
103
106
 
104
- featureFlagAtoms.referrals?.get().then(({ isOn }) => {
105
- if (!isOn) return
106
- kyc.load()
107
- referrals.load()
108
- })
109
-
110
107
  featureFlagAtoms.topMovers?.get().then(({ isOn }) => {
111
108
  if (!isOn) return
112
109
  topMoversMonitor.start()
@@ -126,18 +123,16 @@ const createExodus = ({ adapters, config, port }) => {
126
123
  await Promise.all([
127
124
  //
128
125
  assetsModule.clear(),
129
- walletAccounts.clear(),
130
126
  blockchainMetadata.clear(),
131
127
  enabledAssets.clear(),
132
- connectedOrigins.clear(),
133
128
  featureFlags.clear(),
134
- languageAtom.set(undefined),
135
129
  ])
136
130
 
137
131
  // TEMP: dont wait to clear as encrypted storage is not yet unlocked
138
132
  personalNotes.clear()
139
- nfts.clear()
133
+ })
140
134
 
135
+ application.on('clear', () => {
141
136
  port.emit('clear')
142
137
  })
143
138
 
@@ -153,7 +148,11 @@ const createExodus = ({ adapters, config, port }) => {
153
148
  lifecycleEvents: headlessConfig?.attachAtomsLifecycleEvents,
154
149
  })
155
150
 
156
- attachPlugins({ application, plugins: ioc.getByType('plugin') })
151
+ attachPlugins({
152
+ application,
153
+ plugins: ioc.getByType('plugin'),
154
+ logger: ioc.get('createLogger')('attachPlugins'),
155
+ })
157
156
 
158
157
  return createApi({ ioc, port })
159
158
  }
package/src/ioc.js CHANGED
@@ -15,7 +15,6 @@ const createIOC = ({ adapters, config }) => {
15
15
  const { readOnlyAtoms: readOnlyAtomsConfig, devModeAtoms: devModeAtomsConfig } = config.ioc ?? {}
16
16
 
17
17
  const ioc = createIocContainer({ logger: createLogger('exodus:ioc') })
18
- const dependencies = createDependencies({ adapters, config })
19
18
 
20
19
  const preprocessors = [
21
20
  logify({ createLogger }),
@@ -40,9 +39,13 @@ const createIOC = ({ adapters, config }) => {
40
39
  registerMultiple([dependency])
41
40
  }
42
41
 
43
- registerMultiple(dependencies)
42
+ const use = (module) => {
43
+ registerMultiple(module.definitions)
44
+ }
45
+
46
+ registerMultiple(createDependencies({ adapters, config }))
44
47
 
45
- return { ...ioc, register, registerMultiple }
48
+ return { ...ioc, register, registerMultiple, use }
46
49
  }
47
50
 
48
51
  export default createIOC
@@ -0,0 +1,13 @@
1
+ const abTestingApi = ({ abTesting }) => ({
2
+ abTesting: {
3
+ trackEvent: abTesting.trackEvent,
4
+ updateVariant: abTesting.updateVariant,
5
+ },
6
+ })
7
+
8
+ export default {
9
+ id: 'abTestingApi',
10
+ type: 'api',
11
+ factory: abTestingApi,
12
+ dependencies: ['abTesting'],
13
+ }
@@ -0,0 +1,26 @@
1
+ import { abTestingAtomDefinition } from '@exodus/ab-testing/atoms'
2
+ import abTestingDefinition from '@exodus/ab-testing/module'
3
+
4
+ import abTestingApiDefinition from './api'
5
+ import abTestingPluginDefinition from './plugin'
6
+
7
+ const abTesting = () => {
8
+ return {
9
+ id: 'abTesting',
10
+ definitions: [
11
+ {
12
+ definition: abTestingAtomDefinition,
13
+ storage: { namespace: 'abTesting' },
14
+ aliases: [{ implementationId: 'unsafeStorage', interfaceId: 'storage' }],
15
+ },
16
+ {
17
+ definition: abTestingDefinition,
18
+ writesAtom: ['abTestingAtom'],
19
+ },
20
+ { definition: abTestingPluginDefinition },
21
+ { definition: abTestingApiDefinition },
22
+ ],
23
+ }
24
+ }
25
+
26
+ export default abTesting
@@ -0,0 +1,21 @@
1
+ const abTestingLifecyclePlugin = ({ abTesting, featureFlagAtoms }) => {
2
+ const onStart = async () => {
3
+ featureFlagAtoms.abTesting?.get().then(({ isOn }) => {
4
+ if (!isOn) return
5
+ abTesting.load()
6
+ })
7
+ }
8
+
9
+ const onClear = async () => {
10
+ await abTesting.clear()
11
+ }
12
+
13
+ return { onStart, onClear }
14
+ }
15
+
16
+ export default {
17
+ id: 'abTestingLifecyclePlugin',
18
+ type: 'plugin',
19
+ factory: abTestingLifecyclePlugin,
20
+ dependencies: ['abTesting', 'featureFlagAtoms'],
21
+ }
@@ -0,0 +1,23 @@
1
+ const connectedOriginsApi = ({ connectedOrigins, connectedOriginsAtom }) => ({
2
+ connectedOrigins: {
3
+ get: connectedOriginsAtom.get,
4
+ add: connectedOrigins.add,
5
+ clear: connectedOrigins.clear,
6
+ untrust: connectedOrigins.untrust,
7
+ isTrusted: connectedOrigins.isTrusted,
8
+ isAutoApprove: connectedOrigins.isAutoApprove,
9
+ setFavorite: connectedOrigins.setFavorite,
10
+ setAutoApprove: connectedOrigins.setAutoApprove,
11
+ connect: connectedOrigins.connect,
12
+ disconnect: connectedOrigins.disconnect,
13
+ updateConnection: connectedOrigins.updateConnection,
14
+ clearConnections: connectedOrigins.clearConnections,
15
+ },
16
+ })
17
+
18
+ export default {
19
+ id: 'connectedOriginsApi',
20
+ type: 'api',
21
+ factory: connectedOriginsApi,
22
+ dependencies: ['connectedOrigins', 'connectedOriginsAtom'],
23
+ }
@@ -0,0 +1,31 @@
1
+ import { connectedOriginsAtomDefinition } from '@exodus/connected-origins/atoms'
2
+ import connectedOriginsDefinition from '@exodus/connected-origins/module'
3
+
4
+ import connectedOriginsApiDefinition from './api'
5
+ import connectedOriginsPluginDefinition from './plugin'
6
+
7
+ const connectedOrigins = () => {
8
+ return {
9
+ id: 'connectedOrigins',
10
+ definitions: [
11
+ {
12
+ definition: connectedOriginsAtomDefinition,
13
+ storage: { namespace: 'connectedOrigins' },
14
+ aliases: [
15
+ {
16
+ implementationId: 'unsafeStorage',
17
+ interfaceId: 'storage',
18
+ },
19
+ ],
20
+ },
21
+ {
22
+ definition: connectedOriginsDefinition,
23
+ writesAtoms: ['connectedOriginsAtom'],
24
+ },
25
+ { definition: connectedOriginsPluginDefinition },
26
+ { definition: connectedOriginsApiDefinition },
27
+ ],
28
+ }
29
+ }
30
+
31
+ export default connectedOrigins
@@ -0,0 +1,18 @@
1
+ const connectedOriginsPlugin = ({ connectedOrigins }) => {
2
+ const onUnlock = async () => {
3
+ await connectedOrigins.load()
4
+ }
5
+
6
+ const onClear = async () => {
7
+ await connectedOrigins.clear()
8
+ }
9
+
10
+ return { onUnlock, onClear }
11
+ }
12
+
13
+ export default {
14
+ id: 'connectedOriginsLifecyclePlugin',
15
+ type: 'plugin',
16
+ factory: connectedOriginsPlugin,
17
+ dependencies: ['connectedOrigins'],
18
+ }
@@ -0,0 +1,14 @@
1
+ const kycApi = ({ kyc }) => ({
2
+ kyc: {
3
+ start: kyc.start,
4
+ sync: kyc.sync,
5
+ requestKycToken: kyc.requestKycToken,
6
+ },
7
+ })
8
+
9
+ export default {
10
+ id: 'kycApi',
11
+ type: 'api',
12
+ factory: kycApi,
13
+ dependencies: ['kyc'],
14
+ }
@@ -0,0 +1,19 @@
1
+ import { kycAtomDefinition } from '@exodus/kyc/atoms'
2
+ import kycDefinition from '@exodus/kyc/module'
3
+
4
+ import kycApi from './api'
5
+ import kycPlugin from './plugin'
6
+
7
+ const kyc = () => {
8
+ return {
9
+ id: 'kyc',
10
+ definitions: [
11
+ { definition: kycAtomDefinition },
12
+ { definition: kycDefinition, writesAtoms: ['kycAtom'] },
13
+ { definition: kycPlugin },
14
+ { definition: kycApi },
15
+ ],
16
+ }
17
+ }
18
+
19
+ export default kyc
@@ -0,0 +1,21 @@
1
+ const kycPlugin = ({ kyc, featureFlagAtoms }) => {
2
+ const onUnlock = () => {
3
+ featureFlagAtoms.referrals?.get().then(({ isOn }) => {
4
+ if (!isOn) return
5
+ kyc.load()
6
+ })
7
+ }
8
+
9
+ const onUnload = () => {
10
+ kyc.stop()
11
+ }
12
+
13
+ return { onUnlock, onUnload }
14
+ }
15
+
16
+ export default {
17
+ id: 'kycLifecyclePlugin',
18
+ type: 'plugin',
19
+ factory: kycPlugin,
20
+ dependencies: ['kyc', 'featureFlagAtoms'],
21
+ }
@@ -0,0 +1,13 @@
1
+ const localeApi = ({ languageAtom, currencyAtom }) => ({
2
+ locale: {
3
+ setLanguage: (value) => languageAtom.set(value),
4
+ setCurrency: (value) => currencyAtom.set(value),
5
+ },
6
+ })
7
+
8
+ export default {
9
+ id: 'localeApi',
10
+ type: 'api',
11
+ factory: localeApi,
12
+ dependencies: ['languageAtom', 'currencyAtom'],
13
+ }
@@ -0,0 +1,48 @@
1
+ import { createFusionAtomFactory, createStorageAtomFactory } from '@exodus/atoms'
2
+
3
+ import localeApiDefinition from './api'
4
+ import localePluginDefinition from './plugin'
5
+
6
+ const locale = () => {
7
+ return {
8
+ id: 'locale',
9
+ definitions: [
10
+ {
11
+ definition: {
12
+ id: 'currencyAtom',
13
+ type: 'atom',
14
+ factory: ({ fusion, config }) =>
15
+ createFusionAtomFactory({ fusion })({
16
+ path: `private.currency`,
17
+ defaultValue: config.defaultValue,
18
+ }),
19
+ dependencies: ['fusion', 'config'],
20
+ },
21
+ },
22
+ {
23
+ definition: {
24
+ id: 'languageAtom',
25
+ type: 'atom',
26
+ factory: ({ storage, config }) =>
27
+ createStorageAtomFactory({ storage })({
28
+ key: 'language',
29
+ defaultValue: config.defaultValue,
30
+ isSoleWriter: true,
31
+ }),
32
+ dependencies: ['storage', 'config'],
33
+ },
34
+ aliases: [
35
+ {
36
+ implementationId: 'unsafeStorage',
37
+ interfaceId: 'storage',
38
+ },
39
+ ],
40
+ storage: { namespace: 'locale' },
41
+ },
42
+ { definition: localePluginDefinition },
43
+ { definition: localeApiDefinition },
44
+ ],
45
+ }
46
+ }
47
+
48
+ export default locale
@@ -0,0 +1,14 @@
1
+ const localePlugin = ({ languageAtom }) => {
2
+ const onClear = async () => {
3
+ await languageAtom.set(undefined)
4
+ }
5
+
6
+ return { onClear }
7
+ }
8
+
9
+ export default {
10
+ id: 'localeLifecyclePlugin',
11
+ type: 'plugin',
12
+ factory: localePlugin,
13
+ dependencies: ['languageAtom'],
14
+ }
@@ -0,0 +1,13 @@
1
+ const createNftsApi = ({ nfts, nftsMonitor }) => ({
2
+ nfts: {
3
+ upsertConfig: nfts.upsertConfig,
4
+ setMonitorInterval: nftsMonitor.setInterval,
5
+ },
6
+ })
7
+
8
+ export default {
9
+ id: 'createNftsApi',
10
+ type: 'api',
11
+ factory: createNftsApi,
12
+ dependencies: ['nfts', 'nftsMonitor'],
13
+ }
@@ -0,0 +1,33 @@
1
+ import { nftsCacheAtomDefinition, nftsConfigsAtomDefinition } from '@exodus/nfts/atoms'
2
+ import nftsModuleDefinition from '@exodus/nfts/module'
3
+ import nftsMonitorDefinition from '@exodus/nfts/monitor'
4
+
5
+ import nftsApiDefinition from './api'
6
+ import nftsPluginDefinition from './plugin'
7
+
8
+ // TODO: Add type to module definitions
9
+ const nfts = () => {
10
+ return {
11
+ id: 'nfts',
12
+ definitions: [
13
+ {
14
+ definition: { type: 'atom', ...nftsCacheAtomDefinition },
15
+ storage: { namespace: 'nftsCache' },
16
+ },
17
+ {
18
+ definition: { type: 'atom', ...nftsConfigsAtomDefinition },
19
+ aliases: [{ implementationId: 'unsafeStorage', interfaceId: 'storage' }],
20
+ storage: { namespace: 'nfts-config' },
21
+ },
22
+ {
23
+ definition: { type: 'monitor', ...nftsMonitorDefinition },
24
+ writesAtoms: ['nftsCacheAtom'],
25
+ },
26
+ { definition: nftsModuleDefinition },
27
+ { definition: nftsPluginDefinition },
28
+ { definition: nftsApiDefinition },
29
+ ],
30
+ }
31
+ }
32
+
33
+ export default nfts
@@ -0,0 +1,23 @@
1
+ const nftsLifecyclePlugin = ({ nfts, nftsMonitor, port }) => {
2
+ // TODO: Move to onStart, but needs testing first
3
+ nftsMonitor.on('nfts', (data) => port.emit('nfts', data))
4
+ nftsMonitor.on('nfts-txs', (data) => port.emit('nfts-txs', data))
5
+
6
+ const onUnlock = () => {
7
+ nfts.load()
8
+ nftsMonitor.start()
9
+ }
10
+
11
+ const onClear = () => {
12
+ nfts.clear()
13
+ }
14
+
15
+ return { onUnlock, onClear }
16
+ }
17
+
18
+ export default {
19
+ id: 'nftsLifecyclePlugin',
20
+ type: 'plugin',
21
+ factory: nftsLifecyclePlugin,
22
+ dependencies: ['nfts', 'nftsMonitor', 'port'],
23
+ }
@@ -0,0 +1,13 @@
1
+ const referralsApi = ({ referrals }) => ({
2
+ referrals: {
3
+ setReferredBy: referrals.setReferredBy,
4
+ referralCodeExists: referrals.referralCodeExists,
5
+ },
6
+ })
7
+
8
+ export default {
9
+ id: 'referralsApi',
10
+ type: 'api',
11
+ factory: referralsApi,
12
+ dependencies: ['referrals'],
13
+ }
@@ -0,0 +1,28 @@
1
+ import { referralsAtomDefinition } from '@exodus/referrals/atoms'
2
+ import referralsDefinition from '@exodus/referrals/module'
3
+
4
+ import referralsApi from './api'
5
+ import referralsPlugin from './plugin'
6
+
7
+ const unsafeStorageAlias = { implementationId: 'unsafeStorage', interfaceId: 'storage' }
8
+
9
+ const referrals = ({ namespace = 'referrals', useUnsafeStorage = true } = {}) => {
10
+ const aliases = useUnsafeStorage ? [unsafeStorageAlias] : []
11
+
12
+ return {
13
+ id: 'referrals',
14
+ definitions: [
15
+ { definition: referralsAtomDefinition },
16
+ {
17
+ definition: referralsDefinition,
18
+ storage: { namespace },
19
+ writesAtoms: ['referralsAtom'],
20
+ aliases,
21
+ },
22
+ { definition: referralsPlugin },
23
+ { definition: referralsApi },
24
+ ],
25
+ }
26
+ }
27
+
28
+ export default referrals
@@ -0,0 +1,21 @@
1
+ const referralsPlugin = ({ referrals, featureFlagAtoms }) => {
2
+ const onUnlock = () => {
3
+ featureFlagAtoms.referrals?.get().then(({ isOn }) => {
4
+ if (!isOn) return
5
+ referrals.load()
6
+ })
7
+ }
8
+
9
+ const onUnload = () => {
10
+ referrals.stop()
11
+ }
12
+
13
+ return { onUnlock, onUnload }
14
+ }
15
+
16
+ export default {
17
+ id: 'referralsLifecyclePlugin',
18
+ type: 'plugin',
19
+ factory: referralsPlugin,
20
+ dependencies: ['referrals', 'featureFlagAtoms'],
21
+ }
@@ -0,0 +1,16 @@
1
+ const walletAccountsApi = ({ walletAccounts, enabledWalletAccountsAtom }) => ({
2
+ walletAccounts: {
3
+ create: walletAccounts.create,
4
+ update: walletAccounts.update,
5
+ disable: walletAccounts.disable,
6
+ enable: walletAccounts.enable,
7
+ getEnabled: enabledWalletAccountsAtom.get,
8
+ },
9
+ })
10
+
11
+ export default {
12
+ id: 'walletAccountsApi',
13
+ type: 'api',
14
+ factory: walletAccountsApi,
15
+ dependencies: ['walletAccounts', 'enabledWalletAccountsAtom'],
16
+ }
@@ -0,0 +1,29 @@
1
+ import {
2
+ enabledWalletAccountsAtomDefinition,
3
+ walletAccountsAtomDefinition,
4
+ } from '@exodus/wallet-accounts/atoms'
5
+ import walletAccountsDefinition from '@exodus/wallet-accounts/module'
6
+
7
+ import walletAccountsApiDefinition from './api'
8
+ import walletAccountsPluginDefinition from './plugin'
9
+
10
+ const walletAccounts = () => {
11
+ return {
12
+ id: 'walletAccounts',
13
+ definitions: [
14
+ {
15
+ definition: walletAccountsDefinition,
16
+ writesAtoms: ['walletAccountsAtom'],
17
+ },
18
+ {
19
+ definition: walletAccountsAtomDefinition,
20
+ storage: { namespace: 'walletAccounts' },
21
+ },
22
+ { definition: enabledWalletAccountsAtomDefinition },
23
+ { definition: walletAccountsApiDefinition },
24
+ { definition: walletAccountsPluginDefinition },
25
+ ],
26
+ }
27
+ }
28
+
29
+ export default walletAccounts
@@ -0,0 +1,18 @@
1
+ const walletAccountsPlugin = ({ walletAccounts }) => {
2
+ const onUnlock = async () => {
3
+ await walletAccounts.load()
4
+ }
5
+
6
+ const onClear = async () => {
7
+ await walletAccounts.clear()
8
+ }
9
+
10
+ return { onUnlock, onClear }
11
+ }
12
+
13
+ export default {
14
+ id: 'walletAccountsLifecyclePlugin',
15
+ type: 'plugin',
16
+ factory: walletAccountsPlugin,
17
+ dependencies: ['walletAccounts'],
18
+ }
@@ -1,17 +1,23 @@
1
- import { kebabCase, memoize } from 'lodash'
1
+ import { LifecycleHook } from '../constants'
2
2
 
3
- // e.g. onUnlock -> unlock -> onUnlock, onChangePassphrase -> change-passphrase
4
- const getApplicationHookName = memoize((lifecycleMethod) =>
5
- kebabCase(lifecycleMethod.replace(/^on/, ''))
3
+ const LIFECYCLE_METHOD_TO_HOOK_NAME = Object.fromEntries(
4
+ Object.entries(LifecycleHook).map(([pascalCaseName, hookName]) => [
5
+ `on${pascalCaseName}`,
6
+ hookName,
7
+ ])
6
8
  )
7
9
 
8
- const attachPlugins = ({ plugins, application }) => {
10
+ const attachPlugins = ({ plugins, application, logger }) => {
9
11
  Object.entries(plugins).forEach(([name, lifecycleMethods]) => {
10
12
  const entries = Object.entries(lifecycleMethods || {})
11
13
 
12
14
  for (const [lifecycleMethod, fn] of entries) {
13
- const hookName = getApplicationHookName(lifecycleMethod)
14
- application.hook(hookName, fn)
15
+ const hookName = LIFECYCLE_METHOD_TO_HOOK_NAME[lifecycleMethod]
16
+ if (hookName) {
17
+ application.hook(hookName, fn)
18
+ } else {
19
+ logger.error(`plugin "${name}" declares unsupported lifecycle method "${lifecycleMethod}"`)
20
+ }
15
21
  }
16
22
  })
17
23
  }