@exodus/activity-txs 6.0.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 +6 -0
- package/atoms/activity-txs.js +205 -107
- package/package.json +3 -3
- package/redux/selectors/create-asset-source-base-activity.js +8 -7
- package/redux/selectors/create-batched-asset-source.js +11 -11
- package/redux/selectors/create-with-fiat-activity.js +65 -73
- package/redux/selectors/create-with-nfts-activity.js +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
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
|
+
|
|
6
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)
|
|
7
13
|
|
|
8
14
|
### ⚠ BREAKING CHANGES
|
package/atoms/activity-txs.js
CHANGED
|
@@ -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
|
-
|
|
103
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
228
|
+
if (prevWalletAssetsCount === assetsCount) {
|
|
229
|
+
nextResult[walletAccount] = prevWallet
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
162
232
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
233
|
+
canReuseWallet = false
|
|
234
|
+
}
|
|
235
|
+
}
|
|
166
236
|
|
|
167
|
-
|
|
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
|
-
|
|
252
|
+
nextResult[walletAccount] = nextWallet
|
|
253
|
+
reusedAllWalletObjects = false
|
|
254
|
+
}
|
|
172
255
|
|
|
173
|
-
if (
|
|
256
|
+
if (reusedAllWalletObjects && walletCount === prevWalletCount) {
|
|
174
257
|
return prevResult
|
|
175
258
|
}
|
|
176
259
|
|
|
177
|
-
|
|
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
|
|
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": "6.
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "The activity-txs feature",
|
|
5
5
|
"author": "Exodus Movement, Inc.",
|
|
6
6
|
"repository": {
|
|
@@ -51,7 +51,7 @@
|
|
|
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.
|
|
54
|
+
"@exodus/fiat-ramp": "^15.12.0",
|
|
55
55
|
"@exodus/logger": "^1.2.3",
|
|
56
56
|
"@exodus/nfts": "^9.6.2",
|
|
57
57
|
"@exodus/orders": "^6.0.1",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"access": "public",
|
|
66
66
|
"provenance": false
|
|
67
67
|
},
|
|
68
|
-
"gitHead": "
|
|
68
|
+
"gitHead": "44ef16c9fbb7fa3f5a503b048872145882b54dff"
|
|
69
69
|
}
|
|
@@ -69,7 +69,7 @@ const noop = () => null
|
|
|
69
69
|
const createAssetSourceBaseActivitySelectorDefinition = {
|
|
70
70
|
id: 'createAssetSourceBaseActivity',
|
|
71
71
|
selectorFactory: (
|
|
72
|
-
|
|
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
|
-
|
|
88
|
+
getAssetSelector,
|
|
89
89
|
batchedTxsSelector,
|
|
90
90
|
orderSelector,
|
|
91
91
|
getPersonalNoteByTxIdSelector,
|
|
92
|
-
(
|
|
92
|
+
(getAsset, txs = EMPTY, orderSet, getPersonalNoteByTxId) => {
|
|
93
93
|
const activityResult = []
|
|
94
|
-
const isIndexless =
|
|
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:
|
|
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,
|
|
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: '
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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: '
|
|
63
|
+
{ module: 'assets', selector: 'getAsset' },
|
|
64
64
|
],
|
|
65
65
|
selectorFactory: (
|
|
66
66
|
createLimitedAssetSourceSelector,
|
|
67
67
|
personalNotesWithBatchIdSelector,
|
|
68
|
-
|
|
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
|
-
|
|
78
|
-
(txs, personalNotesWithBatchId,
|
|
77
|
+
getAssetSelector,
|
|
78
|
+
(txs, personalNotesWithBatchId, getAsset) => {
|
|
79
79
|
return personalNotesWithBatchId.size > 0
|
|
80
|
-
? groupAndCollapseBatched({ txs, personalNotesWithBatchId,
|
|
80
|
+
? groupAndCollapseBatched({ txs, personalNotesWithBatchId, getAsset })
|
|
81
81
|
: txs
|
|
82
82
|
}
|
|
83
83
|
),
|
|
@@ -5,7 +5,7 @@ import { TX_TYPES } from '../constants/tx-types.js'
|
|
|
5
5
|
import { ORDER_STATUS, ORDER_TYPES } from '@exodus/models/lib/fiat-order/constants.js'
|
|
6
6
|
|
|
7
7
|
const selectorFactory =
|
|
8
|
-
(visibleOrdersSelector
|
|
8
|
+
(visibleOrdersSelector) =>
|
|
9
9
|
({ createActivitySelector }) => {
|
|
10
10
|
assert(createActivitySelector, 'please provide activitySelector')
|
|
11
11
|
|
|
@@ -14,91 +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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
let hasChanges = false
|
|
23
|
+
const pendingFiatOrdersWithoutTx = []
|
|
24
|
+
const fiatOrdersWithoutTx = []
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
if (displayFiatOrdersWithoutTx) {
|
|
27
|
+
for (const order of fiatOrders) {
|
|
28
|
+
if (order.txId) continue
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
const isRelatedBuyOrder =
|
|
31
|
+
order.toWalletAccount === walletAccount && order.toAsset === assetName
|
|
32
|
+
const isRelatedSellOrder =
|
|
33
|
+
order.fromWalletAccount === walletAccount && order.fromAsset === assetName
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
35
|
+
if (isRelatedBuyOrder || isRelatedSellOrder) {
|
|
36
|
+
fiatOrdersWithoutTx.push(order)
|
|
37
|
+
if (order.exodusStatus === ORDER_STATUS.in_progress) {
|
|
38
|
+
pendingFiatOrdersWithoutTx.push(order)
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
}
|
|
42
|
+
}
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
const resultActivity = []
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
for (const activityItem of activity) {
|
|
47
|
+
const fiatOrdersByTx = fiatOrders.getAllByTxId(activityItem.txId)
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
if (fiatOrdersByTx.length > 0) {
|
|
50
|
+
hasChanges = true
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
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
|
|
65
|
+
|
|
66
|
+
return activityItem.tx?.coinAmount?.abs().equals(orderAmount)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
if (matchingOrderIndex !== -1) {
|
|
70
|
+
hasChanges = true
|
|
71
|
+
pendingFiatOrdersWithoutTx.splice(matchingOrderIndex, 1)
|
|
72
|
+
continue
|
|
78
73
|
}
|
|
79
|
-
|
|
80
|
-
resultActivity.push(activityItem)
|
|
81
74
|
}
|
|
82
|
-
}
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
hasChanges = true
|
|
86
|
-
resultActivity.push({
|
|
87
|
-
id: `${assetName}.fiat-order-without-tx.${fiatOrder.orderId}`,
|
|
88
|
-
assetName,
|
|
89
|
-
walletAccount,
|
|
90
|
-
fiatOrder,
|
|
91
|
-
type: TX_TYPES.FIAT,
|
|
92
|
-
pending: fiatOrder.exodusStatus === ORDER_STATUS.in_progress,
|
|
93
|
-
date: fiatOrder.date || new Date(),
|
|
94
|
-
})
|
|
76
|
+
resultActivity.push(activityItem)
|
|
95
77
|
}
|
|
78
|
+
}
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
})
|
|
100
91
|
}
|
|
101
|
-
|
|
92
|
+
|
|
93
|
+
if (!hasChanges) return activity
|
|
94
|
+
|
|
95
|
+
return resultActivity
|
|
96
|
+
})
|
|
102
97
|
},
|
|
103
98
|
(deps) => JSON.stringify(deps)
|
|
104
99
|
)
|
|
@@ -107,10 +102,7 @@ const selectorFactory =
|
|
|
107
102
|
const createWithFiatActivitySelectorDefinition = {
|
|
108
103
|
id: 'createWithFiatActivity',
|
|
109
104
|
selectorFactory,
|
|
110
|
-
dependencies: [
|
|
111
|
-
{ module: 'fiatRamp', selector: 'visibleOrders', optional: true },
|
|
112
|
-
{ module: 'assets', selector: 'all' },
|
|
113
|
-
],
|
|
105
|
+
dependencies: [{ module: 'fiatRamp', selector: 'visibleOrders', optional: true }],
|
|
114
106
|
}
|
|
115
107
|
|
|
116
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,
|
|
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
|
-
|
|
24
|
+
getAssetSelector,
|
|
25
25
|
activitySelector,
|
|
26
|
-
(nftsByTxId, activeNfts,
|
|
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:
|
|
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 =
|
|
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: '
|
|
95
|
+
{ module: 'assets', selector: 'getAsset' },
|
|
96
96
|
],
|
|
97
97
|
}
|
|
98
98
|
|