@exodus/ethereum-api 2.16.0 → 2.17.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.
Files changed (49) hide show
  1. package/package/package.json +33 -0
  2. package/package/src/eth-like-util.js +147 -0
  3. package/package/src/etherscan/account.js +49 -0
  4. package/package/src/etherscan/index.js +48 -0
  5. package/package/src/etherscan/logs.js +16 -0
  6. package/package/src/etherscan/proxy.js +47 -0
  7. package/package/src/etherscan/request.js +25 -0
  8. package/package/src/etherscan/ws.js +88 -0
  9. package/package/src/exodus-eth-server/api.js +242 -0
  10. package/package/src/exodus-eth-server/index.js +36 -0
  11. package/package/src/exodus-eth-server/ws.js +108 -0
  12. package/package/src/fee-monitor/avalanchec.js +12 -0
  13. package/package/src/fee-monitor/bsc.js +12 -0
  14. package/package/src/fee-monitor/ethereum.js +13 -0
  15. package/package/src/fee-monitor/ethereumclassic.js +12 -0
  16. package/package/src/fee-monitor/fantom.js +12 -0
  17. package/package/src/fee-monitor/harmony.js +12 -0
  18. package/package/src/fee-monitor/index.js +7 -0
  19. package/package/src/fee-monitor/polygon.js +12 -0
  20. package/package/src/gas-estimation.js +103 -0
  21. package/package/src/get-balances.js +38 -0
  22. package/package/src/index.js +11 -0
  23. package/package/src/simulate-tx/fetch-tx-preview.js +21 -0
  24. package/package/src/simulate-tx/index.js +2 -0
  25. package/package/src/simulate-tx/simulate-eth-tx.js +86 -0
  26. package/package/src/staking/fantom-staking.js +115 -0
  27. package/package/src/staking/index.js +2 -0
  28. package/package/src/staking/matic-staking.js +159 -0
  29. package/package/src/tx-log/__tests__/assets-for-test-helper.js +30 -0
  30. package/package/src/tx-log/__tests__/bsc-history-return-values-for-test-helper.js +94 -0
  31. package/package/src/tx-log/__tests__/bsc-monitor.integration.test.js +167 -0
  32. package/package/src/tx-log/__tests__/bsc-monitor.test.js +143 -0
  33. package/package/src/tx-log/__tests__/ethereum-history-return-values-for-test-helper.js +357 -0
  34. package/package/src/tx-log/__tests__/ethereum-history-unknown-token-helper.js +612 -0
  35. package/package/src/tx-log/__tests__/ethereum-monitor.integration.test.js +163 -0
  36. package/package/src/tx-log/__tests__/ethereum-monitor.test.js +211 -0
  37. package/package/src/tx-log/__tests__/monitor-test-helper.js +39 -0
  38. package/package/src/tx-log/__tests__/steth-monitor.integration.test.js +91 -0
  39. package/package/src/tx-log/__tests__/uniswap-monitor.integration.test.js +86 -0
  40. package/package/src/tx-log/__tests__/uniswap-monitor.test.js +158 -0
  41. package/package/src/tx-log/__tests__/uniswap-return-values-for-test-helper.js +193 -0
  42. package/package/src/tx-log/ethereum-monitor.js +293 -0
  43. package/package/src/tx-log/index.js +1 -0
  44. package/package/src/tx-log/ws-updates.js +75 -0
  45. package/package/src/websocket/index.android.js +2 -0
  46. package/package/src/websocket/index.ios.js +2 -0
  47. package/package/src/websocket/index.js +23 -0
  48. package/package.json +3 -3
  49. package/exodus-ethereum-api-2.66.66.tgz +0 -0
@@ -0,0 +1,163 @@
1
+ import { EthereumMonitor } from '../ethereum-monitor'
2
+ import { size, omit } from 'lodash'
3
+ import { AccountState } from '@exodus/models'
4
+ import { once } from 'events'
5
+ import { ethereum as feeData } from '@exodus/ethereum-lib/src/fee-data'
6
+ import assetMap from './assets-for-test-helper'
7
+ import { expectSameValue, toBalanceFromTx } from './monitor-test-helper'
8
+ import { create } from '../../exodus-eth-server/api'
9
+ import {
10
+ PublicKeyAddressResolver,
11
+ logger,
12
+ InMemoryAssetClientInterface,
13
+ } from '@exodus/assets-testing'
14
+
15
+ const EXODUS_ETH_SERVER_URL = 'https://geth.a.exodus.io/wallet/v1/'
16
+
17
+ jest.setTimeout(20000)
18
+
19
+ const { ethereum } = assetMap
20
+
21
+ // Replacing default gasPrice to be sure WS will change it
22
+ const dummyGasPrice = ethereum.currency.Gwei(1) // 1 is the min fee...
23
+ const feeDataMock = feeData.update({ gasPrice: dummyGasPrice.toString() })
24
+ expect(feeDataMock.gasPrice).toEqual(dummyGasPrice)
25
+
26
+ export default class EthereumAccountState extends AccountState {
27
+ static defaults = {
28
+ cursor: '',
29
+ balance: ethereum.currency.ZERO,
30
+ tokenBalances: {},
31
+ }
32
+ }
33
+
34
+ ethereum.api = {
35
+ createAccountState: () => EthereumAccountState,
36
+ getConfirmationsNumber: () => 2,
37
+ getFeeData: () => feeDataMock,
38
+ }
39
+
40
+ // Fernando's dev wallet. Profile 1 and Profile 2
41
+ const walletPublicKeys = {
42
+ ethereum: [
43
+ Buffer.from('0273c38a3c31c31b361dc8d6b93e56c316e34991c478d3a14ea3fcd1ab552bc25e', 'hex'),
44
+ Buffer.from('02f33b1edf1016f6720518c776da30567fc7b7052ae6b596dd22909e0a87164f8d', 'hex'),
45
+ ],
46
+ }
47
+
48
+ describe('ethereum monitor', () => {
49
+ test('can start monitor and update txs', async () => {
50
+ const assetClientInterface = new InMemoryAssetClientInterface({
51
+ logger,
52
+ assets: assetMap,
53
+ addressResolver: new PublicKeyAddressResolver({ walletPublicKeys }),
54
+ })
55
+ const server = create(EXODUS_ETH_SERVER_URL)
56
+ const getHistoryV2 = jest.fn(async (...args) => {
57
+ const transactions = await server.getHistoryV2(...args)
58
+ return transactions.filter((tx) => {
59
+ return parseInt(tx.blockNumber, 16) < 14808250
60
+ })
61
+ })
62
+ const monitor = new EthereumMonitor({
63
+ interval: 10000,
64
+ asset: ethereum,
65
+ assetClientInterface,
66
+ logger,
67
+ server: { ...server, getHistoryV2 },
68
+ })
69
+ try {
70
+ await monitor.start()
71
+ expect(logger.error).not.toBeCalled()
72
+ expect(logger.warn).not.toBeCalled()
73
+
74
+ // Sintax sugar
75
+ const getTxLog = (walletAccount, assetName) => {
76
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
77
+ }
78
+ const getState = (walletAccount, assetName) => {
79
+ return assetClientInterface.getAccountState({ walletAccount, assetName })
80
+ }
81
+
82
+ expect((await getTxLog('exodus0', 'ethereum')).size).toEqual(7)
83
+ expect((await getTxLog('exodus0', 'bat')).size).toEqual(1)
84
+ expect((await getTxLog('exodus0', 'tetherusd')).size).toEqual(3)
85
+ expect((await getTxLog('exodus1', 'ethereum')).size).toEqual(1)
86
+
87
+ expectSameValue(
88
+ toBalanceFromTx(await getTxLog('exodus0', 'ethereum')),
89
+ ethereum.currency.defaultUnit('0.051201488965893697') // string due to floating error
90
+ )
91
+ expectSameValue(
92
+ toBalanceFromTx(await getTxLog('exodus0', 'bat')),
93
+ assetMap.bat.currency.defaultUnit(8.42277112)
94
+ )
95
+ expectSameValue(
96
+ toBalanceFromTx(await getTxLog('exodus0', 'tetherusd')),
97
+ assetMap.tetherusd.currency.defaultUnit(7.414404)
98
+ )
99
+ expectSameValue(
100
+ toBalanceFromTx(await getTxLog('exodus1', 'ethereum')),
101
+ ethereum.currency.defaultUnit(0.00821921)
102
+ )
103
+
104
+ // It seems eth balances and tokenBalances are not being filled. Clients needs to read tx like above
105
+ const accountState0 = await getState('exodus0', 'ethereum')
106
+ expectSameValue(accountState0.balance, ethereum.currency.defaultUnit(0))
107
+ expect(accountState0.tokenBalances.steth).toBeDefined()
108
+ const accountState1 = await getState('exodus1', 'ethereum')
109
+ expectSameValue(accountState1.balance, ethereum.currency.defaultUnit(0))
110
+ expect(accountState1.tokenBalances.steth).toBeDefined()
111
+ expect(size(assetClientInterface.states)).toEqual(2)
112
+ } finally {
113
+ await monitor.stop()
114
+ }
115
+ expect(logger.error).not.toBeCalled()
116
+ expect(logger.warn).not.toBeCalled()
117
+ expect(monitor.timer.isRunning).toEqual(false)
118
+ })
119
+
120
+ test('can start and update gas price', async () => {
121
+ // ws is not reliable enough to finish on time.
122
+ const server = create(EXODUS_ETH_SERVER_URL)
123
+ const assetClientInterface = new InMemoryAssetClientInterface({
124
+ logger,
125
+ assets: assetMap,
126
+ addressResolver: new PublicKeyAddressResolver({ walletPublicKeys }),
127
+ })
128
+ const monitor = new EthereumMonitor({
129
+ interval: 10000,
130
+ asset: ethereum,
131
+ assetClientInterface,
132
+ logger,
133
+ server,
134
+ })
135
+ try {
136
+ expect(await assetClientInterface.getFeeData({ assetName: ethereum.name })).toEqual(
137
+ feeDataMock
138
+ )
139
+
140
+ const oncePromise = once(assetClientInterface, 'fee-config-updated')
141
+ await monitor.start()
142
+ await oncePromise
143
+ // Once started, feeData gets updated
144
+ expect(await assetClientInterface.getFeeData({ assetName: ethereum.name })).not.toEqual(
145
+ feeDataMock
146
+ )
147
+ // gas price and origin are the changed ones.
148
+ expect(
149
+ omit(
150
+ await assetClientInterface.getFeeData({ assetName: ethereum.name }),
151
+ 'gasPrice',
152
+ 'origin'
153
+ )
154
+ ).toEqual(omit(feeDataMock, 'gasPrice', 'origin'))
155
+ } finally {
156
+ await monitor.stop()
157
+ }
158
+
159
+ expect(logger.error).not.toBeCalled()
160
+ expect(logger.warn).not.toBeCalled()
161
+ expect(monitor.timer.isRunning).toEqual(false)
162
+ })
163
+ })
@@ -0,0 +1,211 @@
1
+ import { EthereumMonitor } from '../ethereum-monitor'
2
+ import { encodePublic } from '@exodus/ethereum-lib'
3
+ import { toBalanceFromTx, expectSameValue, mockServer } from './monitor-test-helper'
4
+ import assetMap from './assets-for-test-helper'
5
+ import { AccountState } from '@exodus/models'
6
+ import { ethereum as feeData } from '@exodus/ethereum-lib/src/fee-data'
7
+ import historyReturnValuesForTest from './ethereum-history-return-values-for-test-helper'
8
+ import historyReturnValuesUnknownToken from './ethereum-history-unknown-token-helper'
9
+ import { size, cloneDeep } from 'lodash'
10
+ import {
11
+ PublicKeyAddressResolver,
12
+ logger,
13
+ InMemoryAssetClientInterface,
14
+ } from '@exodus/assets-testing'
15
+
16
+ const { ethereum } = assetMap
17
+
18
+ export default class EthereumAccountState extends AccountState {
19
+ static defaults = {
20
+ cursor: '',
21
+ balance: ethereum.currency.ZERO,
22
+ tokenBalances: {},
23
+ }
24
+ }
25
+
26
+ ethereum.api = {
27
+ createAccountState: () => EthereumAccountState,
28
+ getConfirmationsNumber: () => 2,
29
+ getFeeData: () => feeData,
30
+ }
31
+ // Fernando's dev wallet. Profile 1 and Profile 2
32
+ const walletPublicKeys = {
33
+ ethereum: [
34
+ Buffer.from('0273c38a3c31c31b361dc8d6b93e56c316e34991c478d3a14ea3fcd1ab552bc25e', 'hex'),
35
+ Buffer.from('02f33b1edf1016f6720518c776da30567fc7b7052ae6b596dd22909e0a87164f8d', 'hex'),
36
+ ],
37
+ }
38
+
39
+ describe('ethereum monitor', () => {
40
+ test('ethereum can create monitor', () => {
41
+ const assetClientInterface = new InMemoryAssetClientInterface({
42
+ logger,
43
+ assets: assetMap,
44
+ addressResolver: new PublicKeyAddressResolver({ walletPublicKeys }),
45
+ })
46
+ const server = mockServer({})
47
+ const monitor = new EthereumMonitor({
48
+ interval: 10000,
49
+ asset: ethereum,
50
+ assetClientInterface,
51
+ logger,
52
+ server: server,
53
+ })
54
+ expect(monitor.server.getURL()).toEqual('https://mockMe')
55
+
56
+ expect(logger.warn).not.toBeCalled()
57
+ expect(logger.error).not.toBeCalled()
58
+ })
59
+
60
+ test('can start and stop monitor using simulated data', async () => {
61
+ const assetClientInterface = new InMemoryAssetClientInterface({
62
+ logger,
63
+ assets: assetMap,
64
+ addressResolver: new PublicKeyAddressResolver({ walletPublicKeys }),
65
+ })
66
+
67
+ const getHistoryV2Returns = cloneDeep(historyReturnValuesForTest)
68
+ const server = mockServer({ getHistoryV2Returns })
69
+
70
+ const monitor = new EthereumMonitor({
71
+ interval: 10000,
72
+ asset: ethereum,
73
+ assetClientInterface,
74
+ logger,
75
+ server: server,
76
+ })
77
+ await monitor.start()
78
+ await monitor.stop()
79
+ expect(logger.warn).not.toBeCalled()
80
+ expect(logger.error).not.toBeCalled()
81
+
82
+ // Sintax sugar
83
+ const getTxLog = (walletAccount, assetName) => {
84
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
85
+ }
86
+ const getState = (walletAccount, assetName) => {
87
+ return assetClientInterface.getAccountState({ walletAccount, assetName })
88
+ }
89
+
90
+ expect((await getTxLog('exodus0', 'ethereum')).size).toEqual(7)
91
+ expect((await getTxLog('exodus0', 'bat')).size).toEqual(1)
92
+ expect((await getTxLog('exodus0', 'tetherusd')).size).toEqual(3)
93
+ expect((await getTxLog('exodus1', 'ethereum')).size).toEqual(1)
94
+
95
+ expectSameValue(
96
+ toBalanceFromTx(await getTxLog('exodus0', 'ethereum')),
97
+ ethereum.currency.defaultUnit('0.051201488965893697') // string due to floating error
98
+ )
99
+ expectSameValue(
100
+ toBalanceFromTx(await getTxLog('exodus0', 'bat')),
101
+ assetMap.bat.currency.defaultUnit(8.42277112)
102
+ )
103
+ expectSameValue(
104
+ toBalanceFromTx(await getTxLog('exodus0', 'tetherusd')),
105
+ assetMap.tetherusd.currency.defaultUnit(7.414404)
106
+ )
107
+ expectSameValue(
108
+ toBalanceFromTx(await getTxLog('exodus1', 'ethereum')),
109
+ ethereum.currency.defaultUnit(0.00821921)
110
+ )
111
+
112
+ // It seems eth balances and tokenBalances are not being filled. Clients needs to read tx like above
113
+ expectSameValue(
114
+ (await getState('exodus0', 'ethereum')).balance,
115
+ ethereum.currency.defaultUnit(0)
116
+ )
117
+ expect((await getState('exodus0', 'ethereum')).tokenBalances).toEqual({})
118
+ expectSameValue(
119
+ (await getState('exodus1', 'ethereum')).balance,
120
+ ethereum.currency.defaultUnit(0)
121
+ )
122
+ expect((await getState('exodus1', 'ethereum')).tokenBalances).toEqual({})
123
+ expect(size(assetClientInterface.states)).toEqual(2)
124
+
125
+ expect(logger.error).not.toBeCalled()
126
+ expect(logger.warn).not.toBeCalled()
127
+ expect(monitor.timer.isRunning).toEqual(false)
128
+ })
129
+
130
+ test('emits unknown-tokens events', async () => {
131
+ const walletPublicKeys = {
132
+ ethereum: [
133
+ Buffer.from('03f583b2ccd8cc072baabecf9e124783d8112e0ee76b893e8b341e54d910729fbe', 'hex'),
134
+ Buffer.from('03ab984a086ae1c31207e642ce7c17b9f0811a1cdc66cbe8f78f940a646462b3a8', 'hex'),
135
+ ],
136
+ }
137
+ const assetClientInterface = new InMemoryAssetClientInterface({
138
+ logger,
139
+ assets: assetMap,
140
+ addressResolver: new PublicKeyAddressResolver({ walletPublicKeys }),
141
+ })
142
+
143
+ const getHistoryV2Returns = cloneDeep(historyReturnValuesUnknownToken)
144
+ const server = mockServer({ getHistoryV2Returns })
145
+
146
+ const monitor = new EthereumMonitor({
147
+ interval: 10000,
148
+ asset: ethereum,
149
+ assetClientInterface,
150
+ logger,
151
+ server: server,
152
+ })
153
+
154
+ const handler = jest.fn(() => {})
155
+ monitor.on('unknown-tokens', handler)
156
+
157
+ await monitor.start()
158
+ await monitor.stop()
159
+ expect(logger.warn).not.toBeCalled()
160
+ expect(logger.error).not.toBeCalled()
161
+
162
+ const toBalanceFromTx = (txSet) => {
163
+ return txSet.getMutations().slice(-1)[0].balance
164
+ }
165
+
166
+ // Sintax sugar
167
+ const getTxLog = (walletAccount, assetName) => {
168
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
169
+ }
170
+ const getState = (walletAccount, assetName) => {
171
+ return assetClientInterface.getAccountState({ walletAccount, assetName })
172
+ }
173
+
174
+ const expectSameValue = (actual, expected) => {
175
+ expect(actual).toEqual(expected)
176
+ expect(actual.equals(expected)).toEqual(true)
177
+ }
178
+
179
+ expect((await getTxLog('exodus0', 'ethereum')).size).toEqual(25)
180
+
181
+ expectSameValue(
182
+ toBalanceFromTx(await getTxLog('exodus0', 'ethereum')),
183
+ ethereum.currency.defaultUnit('0.040838166275376413') // string due to floating error
184
+ )
185
+
186
+ // It seems eth balances and tokenBalances are not being filled. Clients needs to read tx like above
187
+ expectSameValue(
188
+ (await getState('exodus0', 'ethereum')).balance,
189
+ ethereum.currency.defaultUnit(0)
190
+ )
191
+ expect((await getState('exodus0', 'ethereum')).tokenBalances).toEqual({})
192
+ expect(size(assetClientInterface.states)).toEqual(2)
193
+
194
+ expect(logger.error).not.toBeCalled()
195
+ expect(logger.warn).not.toBeCalled()
196
+ expect(monitor.timer.isRunning).toEqual(false)
197
+
198
+ // unknown token call checks
199
+ expect(handler.mock.calls.length).toBe(1)
200
+ expect(handler.mock.calls[0][0]).toEqual(['0xa2cd3d43c775978a96bdbf12d733d5a1ed94fb18'])
201
+ })
202
+
203
+ test('validate address', () => {
204
+ expect(encodePublic(walletPublicKeys.ethereum[0])).toEqual(
205
+ '0x90E481d9A664ebbE4Be180d9501962255463036d'
206
+ )
207
+ expect(encodePublic(walletPublicKeys.ethereum[1])).toEqual(
208
+ '0xf6c138C36341138dDFC314a11038dA8264B7Ef09'
209
+ )
210
+ })
211
+ })
@@ -0,0 +1,39 @@
1
+ export const toBalanceFromTx = (txSet) => {
2
+ return txSet.getMutations().slice(-1)[0].balance
3
+ }
4
+
5
+ export const expectSameValue = (actual, expected) => {
6
+ expect(actual.toDefault().toString()).toEqual(expected.toDefault().toString())
7
+ expect(actual.equals(expected)).toEqual(true)
8
+ }
9
+
10
+ export const mockServer = ({ getHistoryV2Returns = {}, balances = {}, balancesOf = {} }) => {
11
+ return {
12
+ getURL() {
13
+ return 'https://mockMe'
14
+ },
15
+ ws: { watch: jest.fn(), events: { on: jest.fn() }, open: jest.fn() },
16
+
17
+ getHistoryV2: jest.fn(async (address) => {
18
+ const history = getHistoryV2Returns[address.toLowerCase()]
19
+ const txPage = history?.shift()
20
+ return txPage || []
21
+ }),
22
+
23
+ balanceOf: jest.fn(async (walletAddress, contractAddress) => {
24
+ // jest doesn't to have when-some-arg-then-some-value kind of mocking
25
+ // https://github.com/facebook/jest/issues/6180
26
+ const balance = balancesOf[walletAddress.toLowerCase()]?.[contractAddress.toLowerCase()] || 0
27
+ return {
28
+ confirmed: {
29
+ [contractAddress]: balance,
30
+ },
31
+ }
32
+ }),
33
+
34
+ getBalance: jest.fn(async (walletAddress) => {
35
+ const balance = `${balances[walletAddress.toLowerCase()] || 0}`
36
+ return { confirmed: { value: balance } }
37
+ }),
38
+ }
39
+ }
@@ -0,0 +1,91 @@
1
+ import { EthereumMonitor } from '../ethereum-monitor'
2
+ import { AccountState } from '@exodus/models'
3
+ import { ethereum as feeData } from '@exodus/ethereum-lib/src/fee-data'
4
+ import assetMap from './assets-for-test-helper'
5
+ import { toBalanceFromTx } from './monitor-test-helper'
6
+ import { create } from '../../exodus-eth-server/api'
7
+ import { PlainAddressResolver, logger, InMemoryAssetClientInterface } from '@exodus/assets-testing'
8
+ import { getBalances } from '../../get-balances'
9
+
10
+ const EXODUS_ETH_SERVER_URL = 'https://geth.a.exodus.io/wallet/v1/'
11
+
12
+ jest.setTimeout(20000)
13
+
14
+ const { ethereum } = assetMap
15
+
16
+ // Replacing default gasPrice to be sure WS will change it
17
+ const dummyGasPrice = ethereum.currency.Gwei(1) // 1 is the min fee...
18
+ const feeDataMock = feeData.update({ gasPrice: dummyGasPrice.toString() })
19
+ expect(feeDataMock.gasPrice).toEqual(dummyGasPrice)
20
+
21
+ export default class EthereumAccountState extends AccountState {
22
+ static defaults = {
23
+ cursor: '',
24
+ balance: ethereum.currency.ZERO,
25
+ tokenBalances: {},
26
+ }
27
+ }
28
+
29
+ ethereum.api = {
30
+ createAccountState: () => EthereumAccountState,
31
+ getConfirmationsNumber: () => 2,
32
+ getFeeData: () => feeDataMock,
33
+ }
34
+
35
+ export const walletAddresses = {
36
+ ethereum: ['0xed0eebb4d520a6b0eccc4df8e5214e7a6697c111'],
37
+ }
38
+
39
+ describe('steth ethereum monitor', () => {
40
+ test('can start monitor and validate steth offchain rewards', async () => {
41
+ const assetClientInterface = new InMemoryAssetClientInterface({
42
+ logger,
43
+ assets: assetMap,
44
+ addressResolver: new PlainAddressResolver({ walletAddresses }),
45
+ })
46
+ const server = create(EXODUS_ETH_SERVER_URL)
47
+
48
+ const monitor = new EthereumMonitor({
49
+ interval: 100000,
50
+ asset: ethereum,
51
+ assetClientInterface,
52
+ logger,
53
+ server,
54
+ })
55
+ try {
56
+ await monitor.start()
57
+
58
+ expect(logger.error).not.toBeCalled()
59
+ expect(logger.warn).not.toBeCalled()
60
+ // Sintax sugar
61
+ const getTxLog = (walletAccount, assetName) => {
62
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
63
+ }
64
+ const accountState = await assetClientInterface.getAccountState({
65
+ walletAccount: 'exodus0',
66
+ assetName: 'ethereum',
67
+ })
68
+ const { balance: stEthBalance } = getBalances({
69
+ asset: assetMap.steth,
70
+ txLog: await getTxLog('exodus0', 'steth'),
71
+ accountState,
72
+ })
73
+ const { balance: ethereumBalance } = getBalances({
74
+ asset: assetMap.ethereum,
75
+ txLog: await getTxLog('exodus0', 'ethereum'),
76
+ accountState,
77
+ })
78
+
79
+ const stEthBalanceFromTx = toBalanceFromTx(await getTxLog('exodus0', 'steth'))
80
+
81
+ expect(ethereumBalance.gt(assetMap.ethereum.currency.ZERO)).toEqual(true)
82
+ expect(stEthBalanceFromTx.lt(stEthBalance)).toEqual(true)
83
+ } finally {
84
+ await monitor.stop()
85
+ }
86
+
87
+ expect(logger.error).not.toBeCalled()
88
+ expect(logger.warn).not.toBeCalled()
89
+ expect(monitor.timer.isRunning).toEqual(false)
90
+ })
91
+ })
@@ -0,0 +1,86 @@
1
+ import { EthereumMonitor } from '../ethereum-monitor'
2
+ import { AccountState } from '@exodus/models'
3
+ import { ethereum as feeData } from '@exodus/ethereum-lib/src/fee-data'
4
+ import assetMap from './assets-for-test-helper'
5
+ import { expectSameValue, toBalanceFromTx } from './monitor-test-helper'
6
+ import { create } from '../../exodus-eth-server/api'
7
+ import { PlainAddressResolver, logger, InMemoryAssetClientInterface } from '@exodus/assets-testing'
8
+
9
+ const EXODUS_ETH_SERVER_URL = 'https://geth.a.exodus.io/wallet/v1/'
10
+
11
+ jest.setTimeout(20000)
12
+
13
+ const { ethereum } = assetMap
14
+
15
+ // Replacing default gasPrice to be sure WS will change it
16
+ const dummyGasPrice = ethereum.currency.Gwei(1) // 1 is the min fee...
17
+ const feeDataMock = feeData.update({ gasPrice: dummyGasPrice.toString() })
18
+ expect(feeDataMock.gasPrice).toEqual(dummyGasPrice)
19
+
20
+ export default class EthereumAccountState extends AccountState {
21
+ static defaults = {
22
+ cursor: '',
23
+ balance: ethereum.currency.ZERO,
24
+ tokenBalances: {},
25
+ }
26
+ }
27
+
28
+ ethereum.api = {
29
+ createAccountState: () => EthereumAccountState,
30
+ getConfirmationsNumber: () => 2,
31
+ getFeeData: () => feeDataMock,
32
+ }
33
+
34
+ // Taking address from https://etherscan.io/txs?a=0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
35
+ export const walletAddresses = {
36
+ ethereum: ['0x1f6890cba6150c9f650efd7ee08d7978cfaba0dd'],
37
+ }
38
+
39
+ describe('uniswap ethereum monitor', () => {
40
+ test('can start monitor and update txs', async () => {
41
+ const assetClientInterface = new InMemoryAssetClientInterface({
42
+ logger,
43
+ assets: assetMap,
44
+ addressResolver: new PlainAddressResolver({ walletAddresses }),
45
+ })
46
+ const server = create(EXODUS_ETH_SERVER_URL)
47
+
48
+ const getHistoryV2 = jest.fn(async (...args) => {
49
+ const transactions = await server.getHistoryV2(...args)
50
+ return transactions.filter((tx) => {
51
+ return parseInt(tx.blockNumber, 16) < 14932172
52
+ })
53
+ })
54
+ server.getHistoryV2 = jest.fn(server.getHistoryV2)
55
+ const monitor = new EthereumMonitor({
56
+ interval: 100000,
57
+ asset: ethereum,
58
+ assetClientInterface,
59
+ logger,
60
+ server: { ...server, getHistoryV2 },
61
+ })
62
+ try {
63
+ await monitor.start()
64
+
65
+ expect(logger.error).not.toBeCalled()
66
+ expect(logger.warn).not.toBeCalled()
67
+ // Sintax sugar
68
+ const getTxLog = (walletAccount, assetName) => {
69
+ return assetClientInterface.getTxLog({ walletAccount, assetName })
70
+ }
71
+ const allTxs = await getTxLog('exodus0', 'ethereum')
72
+ expect(allTxs.size).toEqual(47)
73
+
74
+ expectSameValue(
75
+ toBalanceFromTx(await getTxLog('exodus0', 'ethereum')),
76
+ ethereum.currency.defaultUnit('0.082018694406864644')
77
+ ) // string due to floating error
78
+ } finally {
79
+ await monitor.stop()
80
+ }
81
+
82
+ expect(logger.error).not.toBeCalled()
83
+ expect(logger.warn).not.toBeCalled()
84
+ expect(monitor.timer.isRunning).toEqual(false)
85
+ })
86
+ })