@exodus/activity-txs 4.1.1 → 4.1.3
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 +11 -0
- package/package.json +8 -7
- package/redux/selectors/create-asset-source-base-activity.js +24 -43
- package/redux/selectors/create-full-activity.js +5 -2
- package/redux/selectors/create-with-nfts-activity.js +6 -9
- package/redux/utils/activity-formatters/format-nft.js +38 -0
- package/redux/utils/activity-formatters/format-swap-activity.js +93 -0
- package/redux/utils/activity-formatters/format-tx-activity.js +39 -0
- package/redux/utils/activity-formatters/get-shared-props.js +25 -0
- package/redux/utils/activity-formatters/index.js +7 -0
- package/redux/utils/formatters.js +0 -107
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
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
|
+
## [4.1.3](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@4.1.2...@exodus/activity-txs@4.1.3) (2024-05-27)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @exodus/activity-txs
|
|
9
|
+
|
|
10
|
+
## [4.1.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@4.1.1...@exodus/activity-txs@4.1.2) (2024-05-15)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- address security feedback ([#6963](https://github.com/ExodusMovement/exodus-hydra/issues/6963)) ([155bdfe](https://github.com/ExodusMovement/exodus-hydra/commit/155bdfed531685108d828be60c9d1a8be9e785ee))
|
|
15
|
+
- indexless order should be ignored in first loop ([#6965](https://github.com/ExodusMovement/exodus-hydra/issues/6965)) ([83766cd](https://github.com/ExodusMovement/exodus-hydra/commit/83766cd69533d81317ea52ab770541a7ccfde274))
|
|
16
|
+
|
|
6
17
|
## [4.1.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/activity-txs@4.1.0...@exodus/activity-txs@4.1.1) (2024-05-15)
|
|
7
18
|
|
|
8
19
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/activity-txs",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "The activity-txs feature",
|
|
5
5
|
"author": "Exodus Movement, Inc.",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@exodus/models": "^10.1.0",
|
|
37
37
|
"@exodus/module": "^1.2.2",
|
|
38
38
|
"@exodus/multi-account-redux": "^2.0.1",
|
|
39
|
+
"@exodus/typeforce": "^1.19.0",
|
|
39
40
|
"lodash": "^4.17.21",
|
|
40
41
|
"minimalistic-assert": "^1.0.1",
|
|
41
42
|
"reselect": "^3.0.1"
|
|
@@ -44,21 +45,21 @@
|
|
|
44
45
|
"@exodus/assets": "^8.0.95",
|
|
45
46
|
"@exodus/assets-base": "^8.1.10",
|
|
46
47
|
"@exodus/assets-feature": "^5.3.1",
|
|
47
|
-
"@exodus/available-assets": "^8.2.
|
|
48
|
+
"@exodus/available-assets": "^8.2.1",
|
|
48
49
|
"@exodus/bitcoin-plugin": "^1.4.2",
|
|
49
50
|
"@exodus/blockchain-metadata": "^15.3.1",
|
|
50
51
|
"@exodus/connected-origins": "^3.3.1",
|
|
51
52
|
"@exodus/dependency-injection": "^2.2.0",
|
|
52
53
|
"@exodus/dependency-preprocessors": "^5.2.0",
|
|
53
|
-
"@exodus/fiat-ramp": "^10.
|
|
54
|
-
"@exodus/nfts": "^9.
|
|
55
|
-
"@exodus/orders": "^4.
|
|
54
|
+
"@exodus/fiat-ramp": "^10.6.0",
|
|
55
|
+
"@exodus/nfts": "^9.3.0",
|
|
56
|
+
"@exodus/orders": "^4.9.0",
|
|
56
57
|
"@exodus/personal-notes": "^3.6.0",
|
|
57
58
|
"@exodus/redux-dependency-injection": "^3.1.0",
|
|
58
59
|
"@exodus/storage-memory": "^2.1.1",
|
|
59
|
-
"@exodus/wallet-accounts": "^16.5.
|
|
60
|
+
"@exodus/wallet-accounts": "^16.5.1",
|
|
60
61
|
"@types/minimalistic-assert": "^1.0.1",
|
|
61
62
|
"redux": "^4.2.1"
|
|
62
63
|
},
|
|
63
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "5f3e666315d60ebef4051ebffa1518a193a7ec43"
|
|
64
65
|
}
|
|
@@ -1,35 +1,40 @@
|
|
|
1
1
|
import { createSelector } from 'reselect'
|
|
2
2
|
import { memoize } from '@exodus/basic-utils'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { formattersByType } from '../utils/activity-formatters'
|
|
4
|
+
import { formatUnindexedOrder } from '../utils/activity-formatters/format-swap-activity'
|
|
5
5
|
|
|
6
6
|
const EMPTY = Object.freeze([])
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const sentIndex = activityItem.data?.sentIndex
|
|
10
|
-
return sentIndex
|
|
11
|
-
? `${activityItem.assetName}.${activityItem.txId}_${sentIndex}`
|
|
12
|
-
: `${activityItem.assetName}.${activityItem.txId}`
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const groupTxsWithSameOrder = ({ txs, orderSet }) => {
|
|
8
|
+
const groupTxsWithSameOrder = ({ txs, orderSet, isIndexless }) => {
|
|
16
9
|
const getOrder = (tx) => orderSet.getByTxId(tx.txId)
|
|
17
|
-
const exchangeByOrderId =
|
|
10
|
+
const exchangeByOrderId = new Map()
|
|
18
11
|
const result = []
|
|
12
|
+
|
|
19
13
|
for (const tx of txs) {
|
|
20
14
|
const order = getOrder(tx)
|
|
15
|
+
if (isIndexless && order) {
|
|
16
|
+
// Orders are the source of truth for indexless swaps.
|
|
17
|
+
// Skip this tx and add it later as exchanged.
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
|
|
21
21
|
if (order) {
|
|
22
|
-
|
|
22
|
+
if (!exchangeByOrderId.has(order.orderId)) {
|
|
23
|
+
exchangeByOrderId.set(order.orderId, [])
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exchangeByOrderId.get(order.orderId).push(tx)
|
|
23
27
|
} else {
|
|
24
28
|
result.push({ tx, type: 'tx' })
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
exchangeByOrderId.forEach((txsGroup) => {
|
|
29
33
|
const tx = txsGroup.sort((a, b) => b.date.getTime() - a.date.getTime())[0]
|
|
30
34
|
const order = getOrder(tx)
|
|
31
35
|
result.push({ tx, type: 'exchange', order })
|
|
32
36
|
})
|
|
37
|
+
|
|
33
38
|
return result
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -50,24 +55,22 @@ const createAssetSourceBaseActivitySelectorDefinition = {
|
|
|
50
55
|
getPersonalNoteByTxIdSelector,
|
|
51
56
|
(assets, txs = EMPTY, orderSet, getPersonalNoteByTxId) => {
|
|
52
57
|
const activityResult = []
|
|
53
|
-
|
|
54
|
-
const groupedTxs = groupTxsWithSameOrder({ txs, orderSet })
|
|
58
|
+
const isIndexless = assets[assetName]?.baseAsset?.api?.features?.noHistory
|
|
59
|
+
const groupedTxs = groupTxsWithSameOrder({ txs, orderSet, isIndexless })
|
|
55
60
|
|
|
56
61
|
for (const group of groupedTxs) {
|
|
57
62
|
const { tx, order, type: formatterType } = group
|
|
58
63
|
const personalNote = getPersonalNoteByTxId(tx.txId)
|
|
59
|
-
const formattedTx = formattersByType
|
|
60
|
-
assets,
|
|
64
|
+
const formattedTx = formattersByType.get(formatterType)({
|
|
65
|
+
asset: assets[tx.coinName],
|
|
61
66
|
order,
|
|
62
67
|
tx,
|
|
63
68
|
personalNote,
|
|
64
69
|
})
|
|
65
|
-
formattedTx.id = generateActivityId({ activityItem: formattedTx })
|
|
66
70
|
|
|
67
71
|
activityResult.push(formattedTx)
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
const isIndexless = assets[assetName]?.baseAsset?.api?.features?.noHistory
|
|
71
74
|
if (isIndexless) {
|
|
72
75
|
const orderIdsFromActivity = new Set(activityResult.map((tx) => tx.orderId))
|
|
73
76
|
const ordersForIndexlessAssets = [...orderSet].filter(
|
|
@@ -80,30 +83,8 @@ const createAssetSourceBaseActivitySelectorDefinition = {
|
|
|
80
83
|
// Prevent dups by checking if this order already exists.
|
|
81
84
|
if (orderIdsFromActivity.has(order.orderId)) return
|
|
82
85
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
tx: {
|
|
86
|
-
type: TX_TYPES.EXCHANGE,
|
|
87
|
-
date: order.date,
|
|
88
|
-
order,
|
|
89
|
-
pending: !['failed-final', 'complete-verified'].includes(order.status),
|
|
90
|
-
failed: order.status === 'failed-final',
|
|
91
|
-
data: { swapTx: true },
|
|
92
|
-
selfSend: false,
|
|
93
|
-
token: null, // TBD
|
|
94
|
-
bumpType: {},
|
|
95
|
-
coinName: isFromIndexlessAsset ? order.fromAsset : order.toAsset,
|
|
96
|
-
value: isFromIndexlessAsset ? order.fromAmount : order.toAmount,
|
|
97
|
-
coinAmount: isFromIndexlessAsset ? order.fromAmount : order.toAmount,
|
|
98
|
-
txId: isFromIndexlessAsset ? order.fromTxId : order.toTxId,
|
|
99
|
-
sent: isFromIndexlessAsset,
|
|
100
|
-
received: !isFromIndexlessAsset,
|
|
101
|
-
},
|
|
102
|
-
assets,
|
|
103
|
-
order,
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
formattedItem.id = generateActivityId({ activityItem: formattedItem })
|
|
86
|
+
const formattedItem = formatUnindexedOrder({ assetName, assets, order })
|
|
87
|
+
|
|
107
88
|
activityResult.push(formattedItem)
|
|
108
89
|
})
|
|
109
90
|
}
|
|
@@ -6,11 +6,14 @@ const selectorFactory =
|
|
|
6
6
|
createWithConnectionsActivity
|
|
7
7
|
) =>
|
|
8
8
|
// call this once on client-side
|
|
9
|
-
(options =
|
|
9
|
+
(options = Object.create(null)) => {
|
|
10
10
|
let createFullSelector = createAssetSourceBaseActivity
|
|
11
11
|
createFullSelector = createWithNftsActivity({
|
|
12
12
|
createActivitySelector: createFullSelector,
|
|
13
|
-
nftsNetworkNameToAssetName:
|
|
13
|
+
nftsNetworkNameToAssetName:
|
|
14
|
+
(Object.hasOwn(options, 'nftsNetworkNameToAssetName') &&
|
|
15
|
+
options.nftsNetworkNameToAssetName) ||
|
|
16
|
+
Object.create(null),
|
|
14
17
|
})
|
|
15
18
|
createFullSelector = createWithFiatActivity({ createActivitySelector: createFullSelector })
|
|
16
19
|
createFullSelector = createWithConnectionsActivity({
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { memoize } from '@exodus/basic-utils'
|
|
2
2
|
import { createSelector } from 'reselect'
|
|
3
|
-
import { formatUnindexedNftTx } from '../utils/formatters'
|
|
4
3
|
import assert from 'minimalistic-assert'
|
|
5
|
-
import {
|
|
4
|
+
import { formatNftTx, formatUnindexedNftTx } from '../utils/activity-formatters/format-nft'
|
|
6
5
|
|
|
7
6
|
const selectorFactory =
|
|
8
7
|
(createAssetSourceNftTxsById, assetsSelector) =>
|
|
@@ -33,12 +32,9 @@ const selectorFactory =
|
|
|
33
32
|
if (nft) {
|
|
34
33
|
hasChanges = true
|
|
35
34
|
indexedNftTxIds.add(nft.txId)
|
|
36
|
-
resultActivity.push(
|
|
37
|
-
...activityItem,
|
|
38
|
-
|
|
39
|
-
type: TX_TYPES.NFT,
|
|
40
|
-
sent: nft.sent === undefined ? activityItem.sent : nft.sent, // rely on nft metadata for nft txs
|
|
41
|
-
})
|
|
35
|
+
resultActivity.push(
|
|
36
|
+
formatNftTx({ ...activityItem, nft, assetName: activityItem.assetName })
|
|
37
|
+
)
|
|
42
38
|
} else {
|
|
43
39
|
resultActivity.push(activityItem)
|
|
44
40
|
}
|
|
@@ -47,6 +43,7 @@ const selectorFactory =
|
|
|
47
43
|
;[...nftsByTxId.values()]
|
|
48
44
|
.filter(
|
|
49
45
|
(nftTx) =>
|
|
46
|
+
Object.hasOwn(nftsNetworkNameToAssetName, nftTx.network) &&
|
|
50
47
|
assetName === nftsNetworkNameToAssetName[nftTx.network] &&
|
|
51
48
|
!indexedNftTxIds.has(nftTx.txId)
|
|
52
49
|
)
|
|
@@ -54,7 +51,7 @@ const selectorFactory =
|
|
|
54
51
|
hasChanges = true
|
|
55
52
|
const assetName = nftsNetworkNameToAssetName[nftTx.network]
|
|
56
53
|
const asset = assets[assetName]
|
|
57
|
-
const formatted = formatUnindexedNftTx({ nftTx, asset })
|
|
54
|
+
const formatted = formatUnindexedNftTx({ nftTx, assetName: asset.name })
|
|
58
55
|
|
|
59
56
|
resultActivity.push(formatted)
|
|
60
57
|
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getSharedProps } from './get-shared-props'
|
|
2
|
+
import { TX_TYPES } from '../../constants/tx-types'
|
|
3
|
+
|
|
4
|
+
export const formatUnindexedNftTx = ({ assetName, nftTx }) => {
|
|
5
|
+
return {
|
|
6
|
+
...getSharedProps({
|
|
7
|
+
txId: nftTx.txId,
|
|
8
|
+
date: new Date(nftTx.date),
|
|
9
|
+
assetName,
|
|
10
|
+
type: TX_TYPES.NFT,
|
|
11
|
+
id: `nft_${assetName}.${nftTx.txId}`,
|
|
12
|
+
pending: false,
|
|
13
|
+
failed: false,
|
|
14
|
+
sent: nftTx.sent,
|
|
15
|
+
received: !nftTx.sent,
|
|
16
|
+
}),
|
|
17
|
+
nft: nftTx,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const formatNftTx = ({ tx, nft, assetName, ...rest }) => {
|
|
22
|
+
return {
|
|
23
|
+
...rest,
|
|
24
|
+
...getSharedProps({
|
|
25
|
+
txId: nft.txId,
|
|
26
|
+
date: tx.date,
|
|
27
|
+
assetName,
|
|
28
|
+
type: TX_TYPES.NFT,
|
|
29
|
+
id: `nft_${assetName}.${tx.txId}`,
|
|
30
|
+
pending: false,
|
|
31
|
+
failed: tx.failed,
|
|
32
|
+
sent: nft.sent,
|
|
33
|
+
received: !nft.sent,
|
|
34
|
+
}),
|
|
35
|
+
nft,
|
|
36
|
+
tx,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { getSharedProps } from './get-shared-props'
|
|
2
|
+
import { TX_TYPES } from '../../constants/tx-types'
|
|
3
|
+
|
|
4
|
+
const FINAL_ORDER_STATUS = {
|
|
5
|
+
inProgress: 'in-progress',
|
|
6
|
+
failed: 'failed',
|
|
7
|
+
success: 'complete',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const EXODEX_TX_TYPES = {
|
|
11
|
+
PRE_SETUP: 'preSetupTransaction',
|
|
12
|
+
SETUP: 'setupTransaction',
|
|
13
|
+
SWAP: 'swapTransaction',
|
|
14
|
+
CLEANUP: 'cleanupTransaction',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const UI_TX_TYPE_FROM_EXODEX = new Map([
|
|
18
|
+
[EXODEX_TX_TYPES.PRE_SETUP, TX_TYPES.APPROVAL],
|
|
19
|
+
[EXODEX_TX_TYPES.SETUP, TX_TYPES.APPROVAL],
|
|
20
|
+
[EXODEX_TX_TYPES.SWAP, TX_TYPES.EXCHANGE],
|
|
21
|
+
[EXODEX_TX_TYPES.CLEANUP, TX_TYPES.CLEANUP],
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
function getOrderType({ order, tx }) {
|
|
25
|
+
const txInfoFromOrder = tx
|
|
26
|
+
? order.txIds?.find((txFromOrder) => txFromOrder.txId === tx.txId)
|
|
27
|
+
: null
|
|
28
|
+
return UI_TX_TYPE_FROM_EXODEX.get(txInfoFromOrder?.txType) || TX_TYPES.EXCHANGE
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const getSwapSharedProps = ({
|
|
32
|
+
date,
|
|
33
|
+
assetName,
|
|
34
|
+
txId,
|
|
35
|
+
tx,
|
|
36
|
+
order,
|
|
37
|
+
failed,
|
|
38
|
+
displayAmount,
|
|
39
|
+
personalNote,
|
|
40
|
+
pending,
|
|
41
|
+
}) => {
|
|
42
|
+
return {
|
|
43
|
+
...getSharedProps({
|
|
44
|
+
txId,
|
|
45
|
+
date,
|
|
46
|
+
id: `swap_${assetName}.${txId}`,
|
|
47
|
+
assetName,
|
|
48
|
+
type: getOrderType({ tx, order }),
|
|
49
|
+
pending,
|
|
50
|
+
failed,
|
|
51
|
+
sent: order.fromAsset === assetName,
|
|
52
|
+
received: order.toAsset === assetName,
|
|
53
|
+
}),
|
|
54
|
+
tx,
|
|
55
|
+
fromAsset: order.fromAsset,
|
|
56
|
+
toAsset: order.toAsset,
|
|
57
|
+
orderId: order.orderId,
|
|
58
|
+
displayAmount,
|
|
59
|
+
personalNote,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const formatSwapActivity = ({ tx, order, personalNote }) =>
|
|
64
|
+
getSwapSharedProps({
|
|
65
|
+
date: tx.date,
|
|
66
|
+
assetName: order.toAsset,
|
|
67
|
+
txId: tx.txId,
|
|
68
|
+
tx,
|
|
69
|
+
order,
|
|
70
|
+
failed: tx.failed || order.error,
|
|
71
|
+
displayAmount: order.toAmount.abs(),
|
|
72
|
+
personalNote,
|
|
73
|
+
pending: order.exodusStatus === FINAL_ORDER_STATUS.inProgress,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export const formatUnindexedOrder = ({ order, assetName: _assetName, personalNote }) => {
|
|
77
|
+
const isFromIndexlessAsset = order.fromAsset === _assetName
|
|
78
|
+
const assetName = isFromIndexlessAsset ? order.fromAsset : order.toAsset
|
|
79
|
+
let displayAmount = isFromIndexlessAsset ? order.fromAmount : order.toAmount
|
|
80
|
+
displayAmount = displayAmount.abs()
|
|
81
|
+
const txId = isFromIndexlessAsset ? order.fromTxId : order.toTxId
|
|
82
|
+
|
|
83
|
+
return getSwapSharedProps({
|
|
84
|
+
date: order.date,
|
|
85
|
+
assetName,
|
|
86
|
+
txId,
|
|
87
|
+
order,
|
|
88
|
+
failed: order.error,
|
|
89
|
+
displayAmount,
|
|
90
|
+
personalNote,
|
|
91
|
+
pending: order.exodusStatus === FINAL_ORDER_STATUS.inProgress,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { TX_TYPES } from '../../constants/tx-types'
|
|
2
|
+
import { getSharedProps } from './get-shared-props'
|
|
3
|
+
|
|
4
|
+
function getTxType({ tx }) {
|
|
5
|
+
return tx.sent ? TX_TYPES.SENT : TX_TYPES.RECEIVED
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const getMinConfirmations = (asset) => asset.baseAsset.minConfirmations || 1
|
|
9
|
+
|
|
10
|
+
export function isPending(tx, asset) {
|
|
11
|
+
return (
|
|
12
|
+
tx.confirmations < getMinConfirmations(asset) &&
|
|
13
|
+
!tx.dropped && // dropped txs are not pending anymore
|
|
14
|
+
!tx.error // errored txs are not pending anymore
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const generateTxActivityId = ({ tx, asset }) => {
|
|
19
|
+
const sentIndex = tx.data?.sentIndex
|
|
20
|
+
return sentIndex ? `${asset.name}.${tx.txId}_${sentIndex}` : `${asset.name}.${tx.txId}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const formatTxActivity = ({ tx, asset, personalNote }) => {
|
|
24
|
+
return {
|
|
25
|
+
...getSharedProps({
|
|
26
|
+
txId: tx.txId,
|
|
27
|
+
date: tx.date,
|
|
28
|
+
id: generateTxActivityId({ tx, asset }),
|
|
29
|
+
assetName: asset.name,
|
|
30
|
+
type: getTxType({ tx }),
|
|
31
|
+
pending: isPending(tx, asset),
|
|
32
|
+
failed: tx.failed,
|
|
33
|
+
sent: tx.sent,
|
|
34
|
+
received: tx.received,
|
|
35
|
+
}),
|
|
36
|
+
tx,
|
|
37
|
+
personalNote,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import typeforce from '@exodus/typeforce'
|
|
2
|
+
|
|
3
|
+
export const getSharedProps = (opts) => {
|
|
4
|
+
try {
|
|
5
|
+
typeforce(
|
|
6
|
+
opts,
|
|
7
|
+
{
|
|
8
|
+
txId: 'String',
|
|
9
|
+
date: 'Date',
|
|
10
|
+
id: 'String',
|
|
11
|
+
assetName: 'String', // used to filter by asset on client side
|
|
12
|
+
type: 'String',
|
|
13
|
+
pending: 'Boolean', // tx uses tx.pending, order checks order status
|
|
14
|
+
failed: 'Boolean', // tx uses tx.failed, order uses status
|
|
15
|
+
sent: 'Boolean', // tx.uses tx.sent, nft uses nft.sent
|
|
16
|
+
received: 'Boolean', // tx.uses tx.receive, nft uses !nft.sent
|
|
17
|
+
},
|
|
18
|
+
true // no extra props allowed
|
|
19
|
+
)
|
|
20
|
+
} catch {
|
|
21
|
+
console.warn('activity item props dont pass validation', opts)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return opts
|
|
25
|
+
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { TX_TYPES } from '../constants/tx-types'
|
|
2
|
-
|
|
3
|
-
const FINAL_ORDER_STATUS = {
|
|
4
|
-
inProgress: 'in-progress',
|
|
5
|
-
failed: 'failed',
|
|
6
|
-
success: 'complete',
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const EXODEX_TX_TYPES = {
|
|
10
|
-
PRE_SETUP: 'preSetupTransaction',
|
|
11
|
-
SETUP: 'setupTransaction',
|
|
12
|
-
SWAP: 'swapTransaction',
|
|
13
|
-
CLEANUP: 'cleanupTransaction',
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const UI_TX_TYPE_FROM_EXODEX = {
|
|
17
|
-
[EXODEX_TX_TYPES.PRE_SETUP]: TX_TYPES.APPROVAL,
|
|
18
|
-
[EXODEX_TX_TYPES.SETUP]: TX_TYPES.APPROVAL,
|
|
19
|
-
[EXODEX_TX_TYPES.SWAP]: TX_TYPES.EXCHANGE,
|
|
20
|
-
[EXODEX_TX_TYPES.CLEANUP]: TX_TYPES.CLEANUP,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getTxType({ tx, order }) {
|
|
24
|
-
if (order) {
|
|
25
|
-
const txInfoFromOrder = order.txIds?.find((txFromOrder) => txFromOrder.txId === tx.txId)
|
|
26
|
-
return UI_TX_TYPE_FROM_EXODEX[txInfoFromOrder?.txType] || TX_TYPES.EXCHANGE
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return tx.sent ? TX_TYPES.SENT : TX_TYPES.RECEIVED
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const getMinConfirmations = (asset) => asset.baseAsset.minConfirmations || 1
|
|
33
|
-
|
|
34
|
-
export function isPending(tx, asset) {
|
|
35
|
-
return (
|
|
36
|
-
tx.confirmations < getMinConfirmations(asset) &&
|
|
37
|
-
!tx.dropped && // dropped txs are not pending anymore
|
|
38
|
-
!tx.error // errored txs are not pending anymore
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const formatBase = ({ tx, asset, order, personalNote }) => ({
|
|
43
|
-
...tx,
|
|
44
|
-
assetName: asset.name,
|
|
45
|
-
displayTicker: asset.displayTicker,
|
|
46
|
-
type: getTxType({ tx, asset, order }),
|
|
47
|
-
time: tx.date,
|
|
48
|
-
amount: tx.coinAmount.abs().toDefaultNumber(),
|
|
49
|
-
pending: isPending(tx, asset),
|
|
50
|
-
isOrderInProgress: !!order && order.exodusStatus === FINAL_ORDER_STATUS.inProgress,
|
|
51
|
-
confirmations: tx.confirmations,
|
|
52
|
-
failed: tx.failed,
|
|
53
|
-
received: tx.received,
|
|
54
|
-
personalNote,
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
export const formatGroupedExchangeTxs = ({ tx, assets, order, personalNote }) => {
|
|
58
|
-
const toAsset = assets[order.toAsset]
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
...formatBase({ tx, asset: toAsset, order, personalNote }),
|
|
62
|
-
failed: tx.failed || order.error,
|
|
63
|
-
pending: order.exodusStatus === FINAL_ORDER_STATUS.inProgress,
|
|
64
|
-
assetName: toAsset.name,
|
|
65
|
-
txId: tx.txId,
|
|
66
|
-
type: TX_TYPES.EXCHANGE,
|
|
67
|
-
time: tx.date,
|
|
68
|
-
fromAsset: order.fromAsset,
|
|
69
|
-
toAsset: order.toAsset,
|
|
70
|
-
amount: order.toAmount,
|
|
71
|
-
orderId: order.orderId,
|
|
72
|
-
data: {
|
|
73
|
-
swapTx: true,
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const formatGroupedTx = ({ tx, assets, personalNote }) =>
|
|
79
|
-
formatBase({
|
|
80
|
-
tx,
|
|
81
|
-
asset: assets[tx.coinName],
|
|
82
|
-
personalNote,
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
export const formattersByType = {
|
|
86
|
-
exchange: formatGroupedExchangeTxs,
|
|
87
|
-
tx: formatGroupedTx,
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export const formatUnindexedNftTx = ({ asset, nftTx }) => {
|
|
91
|
-
return {
|
|
92
|
-
id: `${asset.name}.${nftTx.txId}`,
|
|
93
|
-
coinName: asset.name,
|
|
94
|
-
assetName: asset.name,
|
|
95
|
-
displayTicker: asset.displayTicker,
|
|
96
|
-
type: TX_TYPES.NFT,
|
|
97
|
-
time: new Date(nftTx.date),
|
|
98
|
-
from: nftTx.from,
|
|
99
|
-
to: nftTx.to,
|
|
100
|
-
sent: Boolean(nftTx.sent),
|
|
101
|
-
received: !nftTx.sent,
|
|
102
|
-
date: new Date(nftTx.date),
|
|
103
|
-
nft: nftTx,
|
|
104
|
-
txId: nftTx.txId,
|
|
105
|
-
selfSend: false,
|
|
106
|
-
}
|
|
107
|
-
}
|