@exodus/activity-txs 6.1.4 → 6.1.5

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,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.5](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@6.1.4...@exodus/activity-txs@6.1.5) (2026-06-25)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **activity-txs:** suppress orphaned chained-swap bridge leg ([#17354](https://github.com/ExodusMovement/exodus-hydra/issues/17354)) ([328b41a](https://github.com/ExodusMovement/exodus-hydra/commit/328b41af15695bf4e4c3057360354fa7b7897d04))
11
+
6
12
  ## [6.1.4](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@6.1.3...@exodus/activity-txs@6.1.4) (2026-06-24)
7
13
 
8
14
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/activity-txs",
3
- "version": "6.1.4",
3
+ "version": "6.1.5",
4
4
  "description": "The activity-txs feature",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "repository": {
@@ -58,7 +58,7 @@
58
58
  "@exodus/orders": "^6.2.2",
59
59
  "@exodus/personal-notes": "^3.9.0",
60
60
  "@exodus/redux-dependency-injection": "^5.0.0",
61
- "@exodus/wallet-accounts": "^20.4.7",
61
+ "@exodus/wallet-accounts": "^20.4.8",
62
62
  "@types/minimalistic-assert": "^1.0.1",
63
63
  "redux": "^4.2.1"
64
64
  },
@@ -66,5 +66,5 @@
66
66
  "access": "public",
67
67
  "provenance": false
68
68
  },
69
- "gitHead": "18b49ce03aa47d805068bce4b76bf834be314747"
69
+ "gitHead": "a6dcf7d36c9d935dc3016b156b8646c4e739d7c6"
70
70
  }
@@ -55,20 +55,50 @@ const classifyTx = ({ tx, orderSet }) => {
55
55
  return isValidTx ? { type: 'exchange', order: foundOrder } : { type: 'tx' }
56
56
  }
57
57
 
58
+ const bridgeAmountKey = (tx) =>
59
+ tx.coinAmount ? `${tx.coinName}:${tx.coinAmount.abs().toBaseString()}` : undefined
60
+
61
+ const amountSign = (tx) => (tx.coinAmount.toBaseString().startsWith('-') ? -1 : 1)
62
+
58
63
  const groupTxsWithSameOrder = ({ txs, orderSet, isIndexless }) => {
59
64
  if (!orderSet) return txs.map((tx) => ({ tx, type: 'tx' }))
60
65
 
66
+ const classifiedTxs = txs.map((tx) => ({ tx, ...classifyTx({ tx, orderSet }) }))
67
+
68
+ const orphanCounterparts = []
69
+ for (const { tx, type } of classifiedTxs) {
70
+ if (type !== 'suppress' || !tx.coinAmount) continue
71
+ const order = orderSet.getByTxId(tx.txId)
72
+ if (!order || order.fromTxId !== order.toTxId) continue
73
+ const key = bridgeAmountKey(tx)
74
+ if (key) orphanCounterparts.push({ key, sign: amountSign(tx), used: false })
75
+ }
76
+
77
+ const isOrphanedBridgeLeg = (tx) => {
78
+ if (orphanCounterparts.length === 0 || !tx.coinAmount) return false
79
+ if (orderSet.getByTxId(tx.txId)) return false
80
+ const key = bridgeAmountKey(tx)
81
+ if (!key) return false
82
+ const sign = amountSign(tx)
83
+ const match = orphanCounterparts.find((c) => !c.used && c.key === key && c.sign === -sign)
84
+ if (!match) return false
85
+ match.used = true
86
+ return true
87
+ }
88
+
61
89
  const exchangeByOrderId = new Map()
62
90
  const result = []
63
91
 
64
- for (const tx of txs) {
65
- const { type, order } = classifyTx({ tx, orderSet })
66
-
92
+ for (const { tx, type, order } of classifiedTxs) {
67
93
  if (type === 'suppress') {
68
94
  // Bridge leg of a chained swap, intentionally omitted from activity.
69
95
  continue
70
96
  }
71
97
 
98
+ if (type === 'tx' && isOrphanedBridgeLeg(tx)) {
99
+ continue
100
+ }
101
+
72
102
  if (isIndexless && order) {
73
103
  // Orders are the source of truth for indexless swaps.
74
104
  // Skip this tx and add it later as exchanged.