@exodus/activity-txs 1.0.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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @exodus/activity-txs
2
+
3
+ ## Usage
4
+
5
+ ioc.use(activityTxs())
@@ -0,0 +1,126 @@
1
+ import { combine, compute } from '@exodus/atoms'
2
+ import { createSelector } from 'reselect'
3
+ import { memoize } from 'lodash'
4
+ import { mapValues } from '@exodus/basic-utils'
5
+ import { createDeepEqualOutputSelector } from '@exodus/core-selectors/utils/deep-equal'
6
+
7
+ const createAssetSourceActivityTxsSelector = memoize(
8
+ ({ assetName, walletAccount }) =>
9
+ createDeepEqualOutputSelector(
10
+ (args) => args.txSet,
11
+ (args) => args.baseAssetAccountState,
12
+ (args) => args.asset,
13
+ (txSet, baseAssetAccountState, asset) =>
14
+ asset.api.getActivityTxs({
15
+ txs: [...txSet],
16
+ asset,
17
+ accountState: baseAssetAccountState,
18
+ })
19
+ ),
20
+ ({ assetName, walletAccount }) => `${assetName}_${walletAccount}`
21
+ )
22
+
23
+ const createAssetSourceTxsSelector = memoize(
24
+ ({ assetName, walletAccount }) =>
25
+ createSelector(
26
+ (args) => args.txSet,
27
+ (txSet) => {
28
+ return [...txSet]
29
+ }
30
+ ),
31
+ ({ assetName, walletAccount }) => `${assetName}_${walletAccount}`
32
+ )
33
+
34
+ const areActivitiesEqual = (obj1 = {}, obj2 = {}) => {
35
+ // Get wallet account keys from both objects
36
+ const obj1WalletAccounts = Object.keys(obj1)
37
+ const obj2WalletAccounts = Object.keys(obj2)
38
+
39
+ // Check if both objects have the same number of wallet accounts
40
+ if (obj1WalletAccounts.length !== obj2WalletAccounts.length) {
41
+ return false
42
+ }
43
+
44
+ // Iterate over each wallet account in obj1
45
+ for (const walletAccount of obj1WalletAccounts) {
46
+ // Check if obj2 has the same wallet account
47
+ if (!obj2.hasOwnProperty(walletAccount)) {
48
+ return false
49
+ }
50
+
51
+ const obj1Assets = obj1[walletAccount] || {}
52
+ const obj2Assets = obj2[walletAccount] || {}
53
+
54
+ const obj1AssetNames = Object.keys(obj1Assets)
55
+ const obj2AssetNames = Object.keys(obj2Assets)
56
+
57
+ if (obj1AssetNames.length !== obj2AssetNames.length) {
58
+ return false
59
+ }
60
+
61
+ // Iterate over each asset in obj1Assets
62
+ for (const assetName of obj1AssetNames) {
63
+ // Check if obj2Assets has the same asset name and the data matches
64
+ if (
65
+ !obj2Assets.hasOwnProperty(assetName) ||
66
+ obj1Assets[assetName] !== obj2Assets[assetName]
67
+ ) {
68
+ return false
69
+ }
70
+ }
71
+ }
72
+
73
+ return true
74
+ }
75
+
76
+ const createActivityTxsAtom = ({ txLogsAtom, accountStatesAtom, assetsModule }) => {
77
+ const inputAtom = combine({
78
+ txLogs: txLogsAtom,
79
+ accountStates: accountStatesAtom,
80
+ })
81
+
82
+ let prevResult
83
+
84
+ const selector = ({ txLogs, accountStates }) => {
85
+ const { value } = txLogs
86
+ const { value: accountStatesValue } = accountStates
87
+ const result = mapValues(value, (accountTxLogs, walletAccount) =>
88
+ mapValues(accountTxLogs, (assetTxSet, assetName) => {
89
+ const asset = assetsModule.getAsset(assetName)
90
+ const baseAssetName = asset.baseAsset.name
91
+ if (asset?.api?.getActivityTxs) {
92
+ return createAssetSourceActivityTxsSelector(assetName)({
93
+ asset,
94
+ txSet: assetTxSet,
95
+ baseAssetAccountState: accountStatesValue?.[walletAccount]?.[baseAssetName],
96
+ })
97
+ } else {
98
+ return createAssetSourceTxsSelector(assetName)({
99
+ txSet: assetTxSet,
100
+ })
101
+ }
102
+ })
103
+ )
104
+ if (areActivitiesEqual(result, prevResult)) {
105
+ return prevResult
106
+ }
107
+
108
+ prevResult = result
109
+
110
+ return result
111
+ }
112
+
113
+ return compute({
114
+ atom: inputAtom,
115
+ selector,
116
+ })
117
+ }
118
+
119
+ const activityTxsAtomDefinition = {
120
+ id: 'activityTxsAtom',
121
+ type: 'atom',
122
+ factory: createActivityTxsAtom,
123
+ dependencies: ['txLogsAtom', 'accountStatesAtom', 'assetsModule'],
124
+ }
125
+
126
+ export default activityTxsAtomDefinition
package/atoms/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as activityTxsAtomDefinition } from './activity-txs'
package/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { activityTxsAtomDefinition } from './atoms'
2
+ import activityTxsPluginDefinition from './plugin'
3
+
4
+ const activityTxs = () => ({
5
+ id: 'activityTxs',
6
+ definitions: [
7
+ {
8
+ definition: activityTxsAtomDefinition,
9
+ },
10
+ { definition: activityTxsPluginDefinition },
11
+ ],
12
+ })
13
+
14
+ export default activityTxs
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@exodus/activity-txs",
3
+ "version": "1.0.0",
4
+ "description": "The activity-txs feature",
5
+ "author": "Exodus Movement Inc.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ExodusMovement/exodus-hydra.git"
9
+ },
10
+ "homepage": "https://github.com/ExodusMovement/exodus-hydra/tree/master/features/activity-txs",
11
+ "license": "UNLICENSED",
12
+ "bugs": {
13
+ "url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Aactivity-txs"
14
+ },
15
+ "main": "index.js",
16
+ "files": [
17
+ "module",
18
+ "atoms",
19
+ "api",
20
+ "plugin",
21
+ "redux",
22
+ "README.md",
23
+ "CHANGELOG.md",
24
+ "!**/__tests__/**",
25
+ "!**/*.test.js"
26
+ ],
27
+ "scripts": {
28
+ "lint": "eslint . --ignore-path ../../.gitignore",
29
+ "lint:fix": "yarn lint --fix",
30
+ "test": "jest"
31
+ },
32
+ "dependencies": {
33
+ "@exodus/atoms": "^5.7.1",
34
+ "@exodus/basic-utils": "^2.1.0",
35
+ "@exodus/core-selectors": "^1.5.0",
36
+ "@exodus/module": "^1.2.0",
37
+ "@exodus/multi-account-redux": "^2.0.0",
38
+ "lodash": "^4.17.21",
39
+ "reselect": "^3.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "@exodus/assets": "^8.0.95",
43
+ "@exodus/assets-base": "^8.1.10",
44
+ "@exodus/assets-feature": "^3.1.1",
45
+ "@exodus/blockchain-metadata": "^14.0.0",
46
+ "@exodus/dependency-injection": "^2.0.1",
47
+ "@exodus/dependency-preprocessors": "^3.1.1",
48
+ "@exodus/models": "^10.1.0",
49
+ "@exodus/personal-notes": "^3.6.0",
50
+ "@exodus/redux-dependency-injection": "^3.0.0",
51
+ "@exodus/storage-memory": "^2.1.1",
52
+ "@exodus/wallet-accounts": "^14.1.0",
53
+ "eslint": "^8.44.0",
54
+ "jest": "^29.1.2",
55
+ "redux": "^4.2.1"
56
+ },
57
+ "gitHead": "12b0a2c583d69e2262471f70e6ac64ed7d7f5553"
58
+ }
@@ -0,0 +1,23 @@
1
+ import createAtomObserver from '@exodus/atoms/src/factories/observer'
2
+
3
+ const createActivityTxsPlugin = ({ activityTxsAtom, port }) => {
4
+ const observer = createAtomObserver({ atom: activityTxsAtom, port, event: 'activityTxs' })
5
+ const onLoad = () => {
6
+ observer.start()
7
+ }
8
+
9
+ const onStop = () => {
10
+ observer.unregister()
11
+ }
12
+
13
+ return { onLoad, onStop }
14
+ }
15
+
16
+ const activityTxsPluginDefinition = {
17
+ id: 'activityTxsPlugin',
18
+ type: 'plugin',
19
+ factory: createActivityTxsPlugin,
20
+ dependencies: ['activityTxsAtom', 'port'],
21
+ }
22
+
23
+ export default activityTxsPluginDefinition
package/redux/id.js ADDED
@@ -0,0 +1 @@
1
+ export default 'activityTxs'
package/redux/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import id from './id'
2
+ import initialState from './initial-state'
3
+ import selectorDefinitions from './selectors'
4
+ import helper from './multi-account-helper'
5
+ import { setAccounts } from '@exodus/multi-account-redux/src/common'
6
+
7
+ const activityTxsReduxDefinition = {
8
+ id,
9
+ type: 'redux-module',
10
+ initialState,
11
+ eventReducers: {
12
+ activityTxs: (state, data) => {
13
+ return setAccounts(state, data)
14
+ },
15
+ },
16
+ selectorDefinitions: [...helper.selectorDefinitions, ...selectorDefinitions],
17
+ }
18
+
19
+ export default activityTxsReduxDefinition
@@ -0,0 +1,5 @@
1
+ import helper from './multi-account-helper'
2
+
3
+ const initialState = helper.createInitialState()
4
+
5
+ export default initialState
@@ -0,0 +1,9 @@
1
+ import { createReduxModuleHelper } from '@exodus/multi-account-redux'
2
+ import id from './id'
3
+
4
+ const helper = createReduxModuleHelper({
5
+ slice: id,
6
+ createInitialPerAssetData: () => [],
7
+ })
8
+
9
+ export default helper
@@ -0,0 +1,74 @@
1
+ import { memoize, get, groupBy, merge } from 'lodash'
2
+ import { createSelector } from 'reselect'
3
+ import { Tx } from '@exodus/models'
4
+
5
+ const collapseBatch = ({ batch, assets }) => {
6
+ const asset = assets[batch[0].coinName]
7
+ const isToken = asset.baseAsset.name !== asset.name
8
+
9
+ const totalBatchValue = batch.reduce(
10
+ (sum, otherTx) => sum.add(otherTx.coinAmount),
11
+ asset.currency.ZERO
12
+ )
13
+ const totalBatchFee = batch.reduce(
14
+ (sum, otherTx) => (otherTx.feeAmount ? sum.add(otherTx.feeAmount) : sum),
15
+ (isToken ? asset.baseAsset : asset).currency.ZERO
16
+ )
17
+ const baseData = batch[0].data || {}
18
+ const batchedIds = batch.map((tx) => tx.txId)
19
+
20
+ const txJSON = batch[0].toJSON()
21
+
22
+ return Tx.fromJSON(
23
+ merge({}, txJSON, {
24
+ coinAmount: totalBatchValue,
25
+ feeAmount: totalBatchFee,
26
+ ...(batchedIds.length > 1 && {
27
+ data: {
28
+ ...baseData,
29
+ batchedIds,
30
+ },
31
+ }),
32
+ }),
33
+ { assets }
34
+ )
35
+ }
36
+
37
+ const groupAndCollapseBatched = ({ txs, personalNotesWithBatchId, assets }) => {
38
+ const { NO_BATCH: unbatchedTransactions = [], ...batches } = groupBy(txs, (tx) =>
39
+ get(personalNotesWithBatchId.get(tx.txId), 'dapp.batch.id', 'NO_BATCH')
40
+ )
41
+ const batchedTransactions = Object.values(batches).map((batch) =>
42
+ collapseBatch({ batch, assets })
43
+ )
44
+
45
+ return [...unbatchedTransactions, ...batchedTransactions]
46
+ }
47
+
48
+ const createBatchedAssetSourceSelectorDefinition = {
49
+ id: 'createBatchedAssetSourceSelector',
50
+ dependencies: [
51
+ { selector: 'createLimitedAssetSourceSelector' },
52
+ { module: 'personalNotes', selector: 'withBatchId' },
53
+ { module: 'assets', selector: 'all' },
54
+ ],
55
+ selectorFactory: (
56
+ createLimitedAssetSourceSelector,
57
+ personalNotesWithBatchIdSelector,
58
+ assetsSelector
59
+ ) =>
60
+ memoize(({ assetName, walletAccount, limit }) =>
61
+ createSelector(
62
+ createLimitedAssetSourceSelector({ assetName, walletAccount }),
63
+ personalNotesWithBatchIdSelector,
64
+ assetsSelector,
65
+ (txs, personalNotesWithBatchId, assets) => {
66
+ return personalNotesWithBatchId.size > 0
67
+ ? groupAndCollapseBatched({ txs, personalNotesWithBatchId, assets })
68
+ : txs
69
+ }
70
+ )
71
+ ),
72
+ }
73
+
74
+ export default createBatchedAssetSourceSelectorDefinition
@@ -0,0 +1,15 @@
1
+ import { memoize } from 'lodash'
2
+ import { createSelector } from 'reselect'
3
+
4
+ const createLimitedAssetSourceSelectorDefinition = {
5
+ id: 'createLimitedAssetSourceSelector',
6
+ dependencies: [{ selector: 'createAssetSourceSelector' }],
7
+ selectorFactory: (createAssetSourceSelector) =>
8
+ memoize(({ assetName, walletAccount, limit }) =>
9
+ createSelector(createAssetSourceSelector({ assetName, walletAccount }), (txs) =>
10
+ limit ? txs.slice(-limit) : txs
11
+ )
12
+ ),
13
+ }
14
+
15
+ export default createLimitedAssetSourceSelectorDefinition
@@ -0,0 +1,4 @@
1
+ import createLimitedAssetSourceSelectorDefinition from './create-limited-asset-source'
2
+ import createBatchedAssetSourceSelector from './create-batched-asset-source'
3
+
4
+ export default [createLimitedAssetSourceSelectorDefinition, createBatchedAssetSourceSelector]