@exodus/activity-txs 5.1.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
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
+ ## [6.1.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@6.0.0...@exodus/activity-txs@6.1.0) (2026-02-05)
7
+
8
+ ### Features
9
+
10
+ - feat: improve activity calculation (#15119)
11
+
12
+ ## [6.0.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@5.0.2...@exodus/activity-txs@6.0.0) (2026-01-26)
13
+
14
+ ### ⚠ BREAKING CHANGES
15
+
16
+ - **orders:** use typescript (#14786)
17
+
18
+ ### Features
19
+
20
+ - feat: update all consumers to use safe strings for error namespaces (#14429)
21
+
22
+ ### Bug Fixes
23
+
24
+ - fix(activity-txs): fiat order duplicates wallet order (#14959)
25
+
26
+ - fix: update NFT transaction sent status calculation (#14359)
27
+
28
+ - refactor(orders)!: use typescript (#14786)
29
+
6
30
  ## [5.1.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@5.0.2...@exodus/activity-txs@5.1.0) (2025-11-19)
7
31
 
8
32
  ### Features
package/README.md CHANGED
@@ -4,7 +4,7 @@ This feature generates user-friendly activity from transactions, orders, fiat or
4
4
 
5
5
  ## How it works
6
6
 
7
- Each asset’s activity consists of a set of transactions transformed using `asset.api.getActivityTxs`, which returns an array of Tx [models](https://github.com/ExodusMovement/exodus-core/blob/cdff25bb962b2301613588c615c5ac09b05f260a/packages/models/src/tx/index.js#L10): `[Tx, Tx, Tx]`.
7
+ Each asset’s activity consists of a set of transactions transformed using `asset.api.getActivityTxs`, which returns an array of Tx [models](https://github.com/ExodusOSS/hydra/blob/7743f4ffcbdb0605deb4943a90ef2e718776c3bf/libraries/models/src/tx/index.ts#L60): `[Tx, Tx, Tx]`.
8
8
 
9
9
  **Most assets don't have this API and simply return the original transactions.**
10
10
 
@@ -43,48 +43,6 @@ const createAssetSourceTxsSelector = memoize(
43
43
  ({ assetName, walletAccount }) => `${assetName}_${walletAccount}`
44
44
  )
45
45
 
46
- const areActivitiesEqual = (obj1 = {}, obj2 = {}) => {
47
- // Get wallet account keys from both objects
48
- const obj1WalletAccounts = Object.keys(obj1)
49
- const obj2WalletAccounts = Object.keys(obj2)
50
-
51
- // Check if both objects have the same number of wallet accounts
52
- if (obj1WalletAccounts.length !== obj2WalletAccounts.length) {
53
- return false
54
- }
55
-
56
- // Iterate over each wallet account in obj1
57
- for (const walletAccount of obj1WalletAccounts) {
58
- // Check if obj2 has the same wallet account
59
- if (!Object.prototype.hasOwnProperty.call(obj2, walletAccount)) {
60
- return false
61
- }
62
-
63
- const obj1Assets = obj1[walletAccount] || {}
64
- const obj2Assets = obj2[walletAccount] || {}
65
-
66
- const obj1AssetNames = Object.keys(obj1Assets)
67
- const obj2AssetNames = Object.keys(obj2Assets)
68
-
69
- if (obj1AssetNames.length !== obj2AssetNames.length) {
70
- return false
71
- }
72
-
73
- // Iterate over each asset in obj1Assets
74
- for (const assetName of obj1AssetNames) {
75
- // Check if obj2Assets has the same asset name and the data matches
76
- if (
77
- !Object.prototype.hasOwnProperty.call(obj2Assets, assetName) ||
78
- obj1Assets[assetName] !== obj2Assets[assetName]
79
- ) {
80
- return false
81
- }
82
- }
83
- }
84
-
85
- return true
86
- }
87
-
88
46
  const createActivityTxsAtom = ({
89
47
  txLogsAtom,
90
48
  accountStatesAtom,
@@ -98,85 +56,225 @@ const createActivityTxsAtom = ({
98
56
  })
99
57
 
100
58
  let prevResult = Object.create(null)
59
+ const perAssetCache = new Map()
60
+ let cacheEntriesCount = 0
61
+ let runIdCounter = 0
62
+
63
+ const getWalletCache = (walletAccount) => {
64
+ let walletCache = perAssetCache.get(walletAccount)
65
+ if (!walletCache) {
66
+ walletCache = new Map()
67
+ perAssetCache.set(walletAccount, walletCache)
68
+ }
101
69
 
102
- const selector = async ({ txLogs, accountStates }) => {
103
- const { value } = txLogs
104
- const { value: accountStatesValue } = accountStates
105
-
106
- // Process each wallet account asynchronously
107
- const resultEntries = await Promise.all(
108
- Object.entries(value).map(async ([walletAccount, accountTxLogs]) => {
109
- // Process each asset within the wallet account asynchronously
110
- const assetEntries = await Promise.all(
111
- Object.entries(accountTxLogs).map(async ([assetName, assetTxSet]) => {
112
- const asset = assetsModule.getAsset(assetName)
113
- const baseAssetName = asset.baseAsset.name
114
-
115
- if (asset?.api?.getActivityTxs) {
116
- let activityOptions
117
-
118
- if (asset?.api?.getActivityOptions) {
119
- const baseAssetAccountState = accountStatesValue?.[walletAccount]?.[baseAssetName]
120
-
121
- const getOptionsForAssetSource = createAssetSourceActivityOptionsSelector({
122
- assetName,
123
- walletAccount,
124
- })
125
-
126
- activityOptions = await getOptionsForAssetSource({
127
- accountState: baseAssetAccountState,
128
- asset,
129
- })
130
- }
131
-
132
- const txs = createAssetSourceTxsSelector({ assetName, walletAccount })({
133
- txSet: assetTxSet,
134
- })
70
+ return walletCache
71
+ }
135
72
 
136
- const getActivityForAssetSource = createAssetSourceActivityTxsSelector({
137
- assetName,
138
- walletAccount,
73
+ function processActivityForAsset(
74
+ walletAccount,
75
+ assetName,
76
+ assetTxSet,
77
+ accountStatesValue,
78
+ runId
79
+ ) {
80
+ const walletCache = getWalletCache(walletAccount)
81
+ const cached = walletCache.get(assetName)
82
+ const asset = assetsModule.getAsset(assetName)
83
+ const baseAssetName = asset.baseAsset.name
84
+ const baseAssetAccountState = accountStatesValue?.[walletAccount]?.[baseAssetName]
85
+
86
+ if (
87
+ cached &&
88
+ cached.txSet === assetTxSet &&
89
+ cached.baseAssetAccountState === baseAssetAccountState
90
+ ) {
91
+ cached.lastSeen = runId
92
+ return cached.output
93
+ }
94
+
95
+ const txs =
96
+ cached && cached.txSet === assetTxSet
97
+ ? cached.txs
98
+ : createAssetSourceTxsSelector({ assetName, walletAccount })({ txSet: assetTxSet })
99
+
100
+ if (asset?.api?.getActivityTxs) {
101
+ let activityOptions
102
+ if (asset?.api?.getActivityOptions) {
103
+ activityOptions =
104
+ cached &&
105
+ cached.baseAssetAccountState === baseAssetAccountState &&
106
+ cached.baseAssetName === baseAssetName
107
+ ? cached.activityOptions
108
+ : createAssetSourceActivityOptionsSelector({ assetName, walletAccount })({
109
+ accountState: baseAssetAccountState,
110
+ asset,
139
111
  })
112
+ }
140
113
 
141
- try {
142
- const activityTxs = await getActivityForAssetSource({
143
- asset,
144
- txs,
145
- activityOptions,
146
- })
147
-
148
- return [assetName, activityTxs]
149
- } catch (e) {
150
- errorTracking.track({
151
- error: e,
152
- context: { assetName },
153
- })
154
- logger.warn(`failed to get activity txs for asset: ${assetName}`, e)
155
- return [assetName, []]
156
- }
114
+ const getActivityForAssetSource = createAssetSourceActivityTxsSelector({
115
+ assetName,
116
+ walletAccount,
117
+ })
118
+
119
+ try {
120
+ const activityTxs = getActivityForAssetSource({
121
+ asset,
122
+ txs,
123
+ activityOptions,
124
+ })
125
+
126
+ if (!cached) cacheEntriesCount += 1
127
+ walletCache.set(assetName, {
128
+ baseAssetName,
129
+ baseAssetAccountState,
130
+ txSet: assetTxSet,
131
+ txs,
132
+ activityOptions,
133
+ output: activityTxs,
134
+ lastSeen: runId,
135
+ })
136
+
137
+ return activityTxs
138
+ } catch (e) {
139
+ errorTracking?.track({
140
+ error: e,
141
+ context: { assetName },
142
+ })
143
+ logger?.warn?.(`failed to get activity txs for asset: ${assetName}`, e)
144
+
145
+ if (!cached) cacheEntriesCount += 1
146
+ walletCache.set(assetName, {
147
+ baseAssetName,
148
+ baseAssetAccountState,
149
+ txSet: assetTxSet,
150
+ txs,
151
+ activityOptions,
152
+ output: [],
153
+ lastSeen: runId,
154
+ })
155
+
156
+ return []
157
+ }
158
+ }
159
+
160
+ if (!cached) cacheEntriesCount += 1
161
+ walletCache.set(assetName, {
162
+ baseAssetName,
163
+ baseAssetAccountState,
164
+ txSet: assetTxSet,
165
+ txs,
166
+ activityOptions: undefined,
167
+ output: txs,
168
+ lastSeen: runId,
169
+ })
170
+
171
+ return txs
172
+ }
173
+
174
+ const selector = ({ txLogs, accountStates }) => {
175
+ const runId = (runIdCounter += 1)
176
+ const txLogsValue = txLogs?.value ?? Object.create(null)
177
+ const accountStatesValue = accountStates?.value ?? Object.create(null)
178
+
179
+ let activeCount = 0
180
+
181
+ const nextResult = Object.create(null)
182
+ let reusedAllWalletObjects = true
183
+ let walletCount = 0
184
+ let prevWalletCount = 0
185
+
186
+ for (const prevWalletAccount in prevResult) {
187
+ if (Object.prototype.hasOwnProperty.call(prevResult, prevWalletAccount)) prevWalletCount += 1
188
+ }
189
+
190
+ for (const walletAccount in txLogsValue) {
191
+ if (!Object.prototype.hasOwnProperty.call(txLogsValue, walletAccount)) continue
192
+ walletCount += 1
193
+
194
+ const accountTxLogs = txLogsValue[walletAccount] ?? Object.create(null)
195
+ const prevWallet = prevResult[walletAccount]
196
+
197
+ let canReuseWallet = Boolean(prevWallet)
198
+ let assetsCount = 0
199
+
200
+ if (prevWallet) {
201
+ for (const assetName in accountTxLogs) {
202
+ if (!Object.prototype.hasOwnProperty.call(accountTxLogs, assetName)) continue
203
+ assetsCount += 1
204
+ activeCount += 1
205
+
206
+ const activityTxs = processActivityForAsset(
207
+ walletAccount,
208
+ assetName,
209
+ accountTxLogs[assetName],
210
+ accountStatesValue,
211
+ runId
212
+ )
213
+
214
+ if (prevWallet[assetName] !== activityTxs) {
215
+ canReuseWallet = false
216
+ break
217
+ }
218
+ }
219
+
220
+ if (canReuseWallet) {
221
+ let prevWalletAssetsCount = 0
222
+ for (const assetName in prevWallet) {
223
+ if (Object.prototype.hasOwnProperty.call(prevWallet, assetName)) {
224
+ prevWalletAssetsCount += 1
157
225
  }
226
+ }
158
227
 
159
- const txs = createAssetSourceTxsSelector({ assetName, walletAccount })({
160
- txSet: assetTxSet,
161
- })
228
+ if (prevWalletAssetsCount === assetsCount) {
229
+ nextResult[walletAccount] = prevWallet
230
+ continue
231
+ }
162
232
 
163
- return [assetName, txs]
164
- })
165
- )
233
+ canReuseWallet = false
234
+ }
235
+ }
166
236
 
167
- return [walletAccount, Object.fromEntries(assetEntries)]
168
- })
169
- )
237
+ const nextWallet = Object.create(null)
238
+ for (const assetName in accountTxLogs) {
239
+ if (!Object.prototype.hasOwnProperty.call(accountTxLogs, assetName)) continue
240
+ activeCount += 1
241
+
242
+ const activityTxs = processActivityForAsset(
243
+ walletAccount,
244
+ assetName,
245
+ accountTxLogs[assetName],
246
+ accountStatesValue,
247
+ runId
248
+ )
249
+ nextWallet[assetName] = activityTxs
250
+ }
170
251
 
171
- const result = Object.fromEntries(resultEntries)
252
+ nextResult[walletAccount] = nextWallet
253
+ reusedAllWalletObjects = false
254
+ }
172
255
 
173
- if (areActivitiesEqual(result, prevResult)) {
256
+ if (reusedAllWalletObjects && walletCount === prevWalletCount) {
174
257
  return prevResult
175
258
  }
176
259
 
177
- prevResult = result
260
+ if (activeCount > 0 && cacheEntriesCount > activeCount * 2) {
261
+ for (const [walletAccount, walletCache] of perAssetCache) {
262
+ for (const [assetName, cached] of walletCache) {
263
+ if (cached.lastSeen !== runId) {
264
+ walletCache.delete(assetName)
265
+ cacheEntriesCount -= 1
266
+ }
267
+ }
268
+
269
+ if (walletCache.size === 0) {
270
+ perAssetCache.delete(walletAccount)
271
+ }
272
+ }
273
+ }
274
+
275
+ prevResult = nextResult
178
276
 
179
- return result
277
+ return nextResult
180
278
  }
181
279
 
182
280
  return compute({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/activity-txs",
3
- "version": "5.1.0",
3
+ "version": "6.1.0",
4
4
  "description": "The activity-txs feature",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "repository": {
@@ -46,15 +46,15 @@
46
46
  "devDependencies": {
47
47
  "@exodus/assets": "^11.0.0",
48
48
  "@exodus/assets-base": "^10.0.0",
49
- "@exodus/assets-feature": "^8.7.1",
50
- "@exodus/available-assets": "^8.8.0",
49
+ "@exodus/assets-feature": "^8.8.0",
50
+ "@exodus/available-assets": "^8.10.1",
51
51
  "@exodus/bitcoin-plugin": "^1.4.2",
52
52
  "@exodus/connected-origins": "^4.3.0",
53
53
  "@exodus/error-tracking": "^3.3.0",
54
- "@exodus/fiat-ramp": "^15.6.0",
54
+ "@exodus/fiat-ramp": "^15.12.0",
55
55
  "@exodus/logger": "^1.2.3",
56
- "@exodus/nfts": "^9.6.1",
57
- "@exodus/orders": "^5.5.2",
56
+ "@exodus/nfts": "^9.6.2",
57
+ "@exodus/orders": "^6.0.1",
58
58
  "@exodus/personal-notes": "^3.9.0",
59
59
  "@exodus/redux-dependency-injection": "^4.1.2",
60
60
  "@exodus/wallet-accounts": "^20.2.1",
@@ -65,5 +65,5 @@
65
65
  "access": "public",
66
66
  "provenance": false
67
67
  },
68
- "gitHead": "c024e571066175633a8803171c93ed122d6dcc01"
68
+ "gitHead": "44ef16c9fbb7fa3f5a503b048872145882b54dff"
69
69
  }
@@ -69,7 +69,7 @@ const noop = () => null
69
69
  const createAssetSourceBaseActivitySelectorDefinition = {
70
70
  id: 'createAssetSourceBaseActivity',
71
71
  selectorFactory: (
72
- assetsSelector,
72
+ getAssetSelector,
73
73
  createBatchedAssetSourceSelector,
74
74
  createOrdersInAccountSelector,
75
75
  getPersonalNoteByTxIdSelector
@@ -85,13 +85,14 @@ const createAssetSourceBaseActivitySelectorDefinition = {
85
85
  ? noop
86
86
  : createOrdersInAccountSelector(walletAccount)
87
87
  return createSelector(
88
- assetsSelector,
88
+ getAssetSelector,
89
89
  batchedTxsSelector,
90
90
  orderSelector,
91
91
  getPersonalNoteByTxIdSelector,
92
- (assets, txs = EMPTY, orderSet, getPersonalNoteByTxId) => {
92
+ (getAsset, txs = EMPTY, orderSet, getPersonalNoteByTxId) => {
93
93
  const activityResult = []
94
- const isIndexless = assets[assetName]?.baseAsset?.api?.features?.noHistory
94
+ const isIndexless =
95
+ assetName && getAsset(assetName)?.baseAsset?.api?.features?.noHistory
95
96
  const groupedTxs = groupTxsWithSameOrder({ txs, orderSet, isIndexless })
96
97
 
97
98
  if (groupedTxs) {
@@ -101,7 +102,7 @@ const createAssetSourceBaseActivitySelectorDefinition = {
101
102
  ? getPersonalNoteByTxId(tx.txId)
102
103
  : undefined
103
104
  const formattedTx = formattersByType.get(formatterType)({
104
- asset: assets[tx.coinName],
105
+ asset: getAsset(tx.coinName),
105
106
  order,
106
107
  tx,
107
108
  personalNote,
@@ -131,7 +132,7 @@ const createAssetSourceBaseActivitySelectorDefinition = {
131
132
  if (orderIdsFromActivity.has(order.orderId)) return
132
133
 
133
134
  activityResult.push({
134
- ...formatUnindexedOrder({ assetName, assets, order }),
135
+ ...formatUnindexedOrder({ assetName, order }),
135
136
  walletAccount,
136
137
  })
137
138
  })
@@ -147,7 +148,7 @@ const createAssetSourceBaseActivitySelectorDefinition = {
147
148
  `${assetName}_${walletAccount}_${JSON.stringify(rest)}`
148
149
  ),
149
150
  dependencies: [
150
- { module: 'assets', selector: 'all' },
151
+ { module: 'assets', selector: 'getAsset' },
151
152
  { selector: 'createBatchedAssetSourceSelector' },
152
153
  { module: 'orders', selector: 'createOrdersInAccount', optional: true },
153
154
  { module: 'personalNotes', selector: 'get', optional: true },
@@ -11,8 +11,7 @@ const { get, groupBy, merge } = lodash
11
11
  // Now, we write the `order.txsIds` array, which contains information about setup, swap transactions, and more.
12
12
  // An order can be matched using any transaction ID from this array.
13
13
  // Each transaction will match the order, but we can deduplicate them later using `order.id` to display single activity item
14
- const collapseBatch = ({ batch, assets }) => {
15
- const asset = assets[batch[0].coinName]
14
+ const collapseBatch = ({ batch, asset }) => {
16
15
  const isToken = asset.baseAsset.name !== asset.name
17
16
 
18
17
  const totalBatchValue = batch.reduce(
@@ -44,13 +43,14 @@ const collapseBatch = ({ batch, assets }) => {
44
43
  )
45
44
  }
46
45
 
47
- const groupAndCollapseBatched = ({ txs, personalNotesWithBatchId, assets }) => {
46
+ const groupAndCollapseBatched = ({ txs, personalNotesWithBatchId, getAsset }) => {
48
47
  const { NO_BATCH: unbatchedTransactions = [], ...batches } = groupBy(txs, (tx) =>
49
48
  get(personalNotesWithBatchId.get(tx.txId), 'dapp.batch.id', 'NO_BATCH')
50
49
  )
51
- const batchedTransactions = Object.values(batches).map((batch) =>
52
- collapseBatch({ batch, assets })
53
- )
50
+ const batchedTransactions = Object.values(batches).map((batch) => {
51
+ const asset = getAsset(batch[0].coinName)
52
+ return collapseBatch({ batch, asset })
53
+ })
54
54
 
55
55
  return [...unbatchedTransactions, ...batchedTransactions]
56
56
  }
@@ -60,12 +60,12 @@ const createBatchedAssetSourceSelectorDefinition = {
60
60
  dependencies: [
61
61
  { selector: 'createLimitedAssetSourceSelector' },
62
62
  { module: 'personalNotes', selector: 'withBatchId', optional: true },
63
- { module: 'assets', selector: 'all' },
63
+ { module: 'assets', selector: 'getAsset' },
64
64
  ],
65
65
  selectorFactory: (
66
66
  createLimitedAssetSourceSelector,
67
67
  personalNotesWithBatchIdSelector,
68
- assetsSelector
68
+ getAssetSelector
69
69
  ) =>
70
70
  memoize(
71
71
  ({ assetName, walletAccount, limit }) =>
@@ -74,10 +74,10 @@ const createBatchedAssetSourceSelectorDefinition = {
74
74
  : createSelector(
75
75
  createLimitedAssetSourceSelector({ assetName, walletAccount, limit }),
76
76
  personalNotesWithBatchIdSelector,
77
- assetsSelector,
78
- (txs, personalNotesWithBatchId, assets) => {
77
+ getAssetSelector,
78
+ (txs, personalNotesWithBatchId, getAsset) => {
79
79
  return personalNotesWithBatchId.size > 0
80
- ? groupAndCollapseBatched({ txs, personalNotesWithBatchId, assets })
80
+ ? groupAndCollapseBatched({ txs, personalNotesWithBatchId, getAsset })
81
81
  : txs
82
82
  }
83
83
  ),
@@ -2,10 +2,10 @@ import { memoize } from '@exodus/basic-utils'
2
2
  import { createSelector } from 'reselect'
3
3
  import assert from 'minimalistic-assert'
4
4
  import { TX_TYPES } from '../constants/tx-types.js'
5
- import { ORDER_STATUS } from '@exodus/models/lib/fiat-order/constants.js'
5
+ import { ORDER_STATUS, ORDER_TYPES } from '@exodus/models/lib/fiat-order/constants.js'
6
6
 
7
7
  const selectorFactory =
8
- (visibleOrdersSelector, assetsSelector) =>
8
+ (visibleOrdersSelector) =>
9
9
  ({ createActivitySelector }) => {
10
10
  assert(createActivitySelector, 'please provide activitySelector')
11
11
 
@@ -14,66 +14,86 @@ const selectorFactory =
14
14
  const activitySelector = createActivitySelector({ assetName, walletAccount, ...rest })
15
15
  assert(activitySelector, 'createActivitySelector must return selector')
16
16
  if (visibleOrdersSelector.isFallback) return activitySelector
17
- return createSelector(
18
- visibleOrdersSelector,
19
- assetsSelector,
20
- activitySelector,
21
- (fiatOrders, assets, activity) => {
22
- if (!fiatOrders) {
23
- return activity
24
- }
17
+ return createSelector(visibleOrdersSelector, activitySelector, (fiatOrders, activity) => {
18
+ if (!fiatOrders) {
19
+ return activity
20
+ }
25
21
 
26
- let hasChanges = false
22
+ let hasChanges = false
23
+ const pendingFiatOrdersWithoutTx = []
24
+ const fiatOrdersWithoutTx = []
27
25
 
28
- const resultActivity = []
29
- for (const activityItem of activity) {
30
- const fiatOrdersByTx = fiatOrders.getAllByTxId(activityItem.txId)
26
+ if (displayFiatOrdersWithoutTx) {
27
+ for (const order of fiatOrders) {
28
+ if (order.txId) continue
31
29
 
32
- if (fiatOrdersByTx.length > 0) {
33
- hasChanges = true
30
+ const isRelatedBuyOrder =
31
+ order.toWalletAccount === walletAccount && order.toAsset === assetName
32
+ const isRelatedSellOrder =
33
+ order.fromWalletAccount === walletAccount && order.fromAsset === assetName
34
34
 
35
- for (const fiatOrder of fiatOrdersByTx) {
36
- resultActivity.push({
37
- ...activityItem,
38
- fiatOrder,
39
- id: `${activityItem.id}.${fiatOrder.orderId}`,
40
- type: TX_TYPES.FIAT,
41
- })
35
+ if (isRelatedBuyOrder || isRelatedSellOrder) {
36
+ fiatOrdersWithoutTx.push(order)
37
+ if (order.exodusStatus === ORDER_STATUS.in_progress) {
38
+ pendingFiatOrdersWithoutTx.push(order)
42
39
  }
43
- } else {
44
- resultActivity.push(activityItem)
45
40
  }
46
41
  }
42
+ }
43
+
44
+ const resultActivity = []
47
45
 
48
- if (displayFiatOrdersWithoutTx) {
49
- for (const fiatOrder of fiatOrders) {
50
- const isOrderWithoutTx = !fiatOrder.txId
51
- const isRelatedBuyOrder =
52
- fiatOrder.toWalletAccount === walletAccount && fiatOrder.toAsset === assetName
53
- const isRelatedSellOrder =
54
- fiatOrder.fromWalletAccount === walletAccount && fiatOrder.fromAsset === assetName
46
+ for (const activityItem of activity) {
47
+ const fiatOrdersByTx = fiatOrders.getAllByTxId(activityItem.txId)
48
+
49
+ if (fiatOrdersByTx.length > 0) {
50
+ hasChanges = true
51
+
52
+ for (const fiatOrder of fiatOrdersByTx) {
53
+ resultActivity.push({
54
+ ...activityItem,
55
+ fiatOrder,
56
+ id: `${activityItem.id}.${fiatOrder.orderId}`,
57
+ type: TX_TYPES.FIAT,
58
+ })
59
+ }
60
+ } else {
61
+ if (activityItem.pending && pendingFiatOrdersWithoutTx.length > 0) {
62
+ const matchingOrderIndex = pendingFiatOrdersWithoutTx.findIndex((order) => {
63
+ const orderAmount =
64
+ order.orderType === ORDER_TYPES.sell ? order.fromAmount : order.toAmount
55
65
 
56
- if (isOrderWithoutTx && (isRelatedBuyOrder || isRelatedSellOrder)) {
66
+ return activityItem.tx?.coinAmount?.abs().equals(orderAmount)
67
+ })
68
+
69
+ if (matchingOrderIndex !== -1) {
57
70
  hasChanges = true
58
- const activityItem = {
59
- id: `${assetName}.fiat-order-without-tx.${fiatOrder.orderId}`,
60
- assetName,
61
- walletAccount,
62
- fiatOrder,
63
- type: TX_TYPES.FIAT,
64
- pending: fiatOrder.exodusStatus === ORDER_STATUS.in_progress,
65
- date: fiatOrder.date || new Date(),
66
- }
67
- resultActivity.push(activityItem)
71
+ pendingFiatOrdersWithoutTx.splice(matchingOrderIndex, 1)
72
+ continue
68
73
  }
69
74
  }
70
- }
71
75
 
72
- if (!hasChanges) return activity
76
+ resultActivity.push(activityItem)
77
+ }
78
+ }
73
79
 
74
- return resultActivity
80
+ for (const fiatOrder of fiatOrdersWithoutTx) {
81
+ hasChanges = true
82
+ resultActivity.push({
83
+ id: `${assetName}.fiat-order-without-tx.${fiatOrder.orderId}`,
84
+ assetName,
85
+ walletAccount,
86
+ fiatOrder,
87
+ type: TX_TYPES.FIAT,
88
+ pending: fiatOrder.exodusStatus === ORDER_STATUS.in_progress,
89
+ date: fiatOrder.date || new Date(),
90
+ })
75
91
  }
76
- )
92
+
93
+ if (!hasChanges) return activity
94
+
95
+ return resultActivity
96
+ })
77
97
  },
78
98
  (deps) => JSON.stringify(deps)
79
99
  )
@@ -82,10 +102,7 @@ const selectorFactory =
82
102
  const createWithFiatActivitySelectorDefinition = {
83
103
  id: 'createWithFiatActivity',
84
104
  selectorFactory,
85
- dependencies: [
86
- { module: 'fiatRamp', selector: 'visibleOrders', optional: true },
87
- { module: 'assets', selector: 'all' },
88
- ],
105
+ dependencies: [{ module: 'fiatRamp', selector: 'visibleOrders', optional: true }],
89
106
  }
90
107
 
91
108
  export default createWithFiatActivitySelectorDefinition
@@ -4,7 +4,7 @@ import assert from 'minimalistic-assert'
4
4
  import { formatNftTx, formatUnindexedNftTx } from '../utils/activity-formatters/format-nft.js'
5
5
 
6
6
  const selectorFactory =
7
- (createAssetSourceNftTxsById, activeNftsSelector, assetsSelector) =>
7
+ (createAssetSourceNftTxsById, activeNftsSelector, getAssetSelector) =>
8
8
  ({ createActivitySelector, nftsNetworkNameToAssetName }) => {
9
9
  assert(createActivitySelector, 'please provide activitySelector')
10
10
  assert(nftsNetworkNameToAssetName, 'please provide nftsNetworkNameToAssetName')
@@ -21,9 +21,9 @@ const selectorFactory =
21
21
  return createSelector(
22
22
  nftsSelector,
23
23
  activeNftsSelector,
24
- assetsSelector,
24
+ getAssetSelector,
25
25
  activitySelector,
26
- (nftsByTxId, activeNfts, assets, activity) => {
26
+ (nftsByTxId, activeNfts, getAsset, activity) => {
27
27
  const spamNftIdsSet = new Set(
28
28
  Object.values(activeNfts)
29
29
  .flat()
@@ -46,7 +46,7 @@ const selectorFactory =
46
46
  formatNftTx({
47
47
  ...activityItem,
48
48
  nft,
49
- asset: assets[activityItem.assetName],
49
+ asset: getAsset(activityItem.assetName),
50
50
  assetName: activityItem.assetName,
51
51
  })
52
52
  )
@@ -69,7 +69,7 @@ const selectorFactory =
69
69
  .forEach((nftTx) => {
70
70
  hasChanges = true
71
71
  const assetName = nftsNetworkNameToAssetName[nftTx.network]
72
- const asset = assets[assetName]
72
+ const asset = getAsset(assetName)
73
73
  const formatted = formatUnindexedNftTx({ nftTx, assetName: asset.name })
74
74
 
75
75
  resultActivity.push(formatted)
@@ -92,7 +92,7 @@ const createWithNftsActivitySelectorDefinition = {
92
92
  dependencies: [
93
93
  { module: 'nfts', selector: 'createAssetSourceNftTxsById', optional: true },
94
94
  { module: 'nfts', selector: 'activeNfts', optional: true },
95
- { module: 'assets', selector: 'all' },
95
+ { module: 'assets', selector: 'getAsset' },
96
96
  ],
97
97
  }
98
98