@exodus/ethereum-api 8.45.6 → 8.46.1
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 +20 -0
- package/package.json +3 -2
- package/src/create-asset-utils.js +10 -0
- package/src/create-asset.js +1 -2
- package/src/exodus-eth-server/index.js +2 -1
- package/src/exodus-eth-server/ws-gateway.js +267 -0
- package/src/tx-create.js +15 -6
- package/src/tx-log/clarity-monitor-v2.js +467 -0
- package/src/tx-send/tx-send.js +0 -15
- package/src/get-fee-async.js +0 -29
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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
|
+
## [8.46.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.46.0...@exodus/ethereum-api@8.46.1) (2025-09-03)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix: tx-create simplification (#6310)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [8.46.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.45.6...@exodus/ethereum-api@8.46.0) (2025-08-21)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* feat: implement clarity websocket gateway client (#5623)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [8.45.6](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.45.5...@exodus/ethereum-api@8.45.6) (2025-08-19)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.46.1",
|
|
4
4
|
"description": "Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Ethereum and EVM-based blockchains",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"@exodus/web3-ethereum-utils": "^4.2.1",
|
|
40
40
|
"bn.js": "^5.2.1",
|
|
41
41
|
"delay": "^4.0.1",
|
|
42
|
+
"eventemitter3": "^4.0.7",
|
|
42
43
|
"events": "^1.1.1",
|
|
43
44
|
"idna-uts46-hx": "^2.3.1",
|
|
44
45
|
"lodash": "^4.17.15",
|
|
@@ -63,5 +64,5 @@
|
|
|
63
64
|
"type": "git",
|
|
64
65
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
65
66
|
},
|
|
66
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "e84949a7310f03feb4893668abd44fe537bf250c"
|
|
67
68
|
}
|
|
@@ -4,6 +4,7 @@ import ms from 'ms'
|
|
|
4
4
|
import { createEvmServer, ValidMonitorTypes } from './exodus-eth-server/index.js'
|
|
5
5
|
import { createEthereumHooks } from './hooks/index.js'
|
|
6
6
|
import { ClarityMonitor } from './tx-log/clarity-monitor.js'
|
|
7
|
+
import { ClarityMonitorV2 } from './tx-log/clarity-monitor-v2.js'
|
|
7
8
|
import { EthereumMonitor } from './tx-log/ethereum-monitor.js'
|
|
8
9
|
import { EthereumNoHistoryMonitor } from './tx-log/ethereum-no-history-monitor.js'
|
|
9
10
|
import { resolveNonce } from './tx-send/nonce-utils.js'
|
|
@@ -128,6 +129,15 @@ export const createHistoryMonitorFactory = ({
|
|
|
128
129
|
...args,
|
|
129
130
|
})
|
|
130
131
|
break
|
|
132
|
+
case 'clarity-v3':
|
|
133
|
+
monitor = new ClarityMonitorV2({
|
|
134
|
+
assetClientInterface,
|
|
135
|
+
interval: ms(monitorInterval || '5m'),
|
|
136
|
+
server,
|
|
137
|
+
rpcBalanceAssetNames,
|
|
138
|
+
...args,
|
|
139
|
+
})
|
|
140
|
+
break
|
|
131
141
|
case 'no-history':
|
|
132
142
|
monitor = new EthereumNoHistoryMonitor({
|
|
133
143
|
assetClientInterface,
|
package/src/create-asset.js
CHANGED
|
@@ -33,7 +33,6 @@ import { createFeeData } from './fee-data-factory.js'
|
|
|
33
33
|
import { createGetBalanceForAddress } from './get-balance-for-address.js'
|
|
34
34
|
import { getBalancesFactory } from './get-balances.js'
|
|
35
35
|
import { getFeeFactory } from './get-fee.js'
|
|
36
|
-
import getFeeAsyncFactory from './get-fee-async.js'
|
|
37
36
|
import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas/index.js'
|
|
38
37
|
import { serverBasedFeeMonitorFactoryFactory } from './server-based-fee-monitor.js'
|
|
39
38
|
import { createStakingApi } from './staking-api.js'
|
|
@@ -258,7 +257,7 @@ export const createAssetFactory = ({
|
|
|
258
257
|
getBalanceForAddress: createGetBalanceForAddress({ asset, server }),
|
|
259
258
|
getConfirmationsNumber: () => confirmationsNumber,
|
|
260
259
|
getDefaultAddressPath: () => defaultAddressPath,
|
|
261
|
-
getFeeAsync:
|
|
260
|
+
getFeeAsync: createTx, // createTx alias, remove me when possible
|
|
262
261
|
getFee,
|
|
263
262
|
getFeeData: () => feeData,
|
|
264
263
|
getKeyIdentifier: createGetKeyIdentifier({
|
|
@@ -13,7 +13,7 @@ import ApiCoinNodesServer from './api-coin-nodes.js'
|
|
|
13
13
|
import ClarityServer from './clarity.js'
|
|
14
14
|
import ClarityServerV2 from './clarity-v2.js'
|
|
15
15
|
|
|
16
|
-
export const ValidMonitorTypes = ['no-history', 'clarity', 'clarity-v2', 'magnifier']
|
|
16
|
+
export const ValidMonitorTypes = ['no-history', 'clarity', 'clarity-v2', 'clarity-v3', 'magnifier']
|
|
17
17
|
|
|
18
18
|
export function createEvmServer({ assetName, serverUrl, monitorType }) {
|
|
19
19
|
assert(assetName, 'assetName is required')
|
|
@@ -25,6 +25,7 @@ export function createEvmServer({ assetName, serverUrl, monitorType }) {
|
|
|
25
25
|
case 'clarity':
|
|
26
26
|
return new ClarityServer({ baseAssetName: assetName, uri: serverUrl })
|
|
27
27
|
case 'clarity-v2':
|
|
28
|
+
case 'clarity-v3':
|
|
28
29
|
return new ClarityServerV2({ baseAssetName: assetName, uri: serverUrl })
|
|
29
30
|
case 'magnifier':
|
|
30
31
|
return create(serverUrl, assetName)
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createConsoleLogger } from '@exodus/asset-lib'
|
|
2
|
+
import WebSocket from '@exodus/fetch/websocket'
|
|
3
|
+
import EventEmitter from 'eventemitter3'
|
|
4
|
+
import assert from 'minimalistic-assert'
|
|
5
|
+
|
|
6
|
+
const createSubscriptionName = (network) => {
|
|
7
|
+
return `v1/transactions/${network}`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const createSubscriptionKey = ({ subscriptionName, address }) => {
|
|
11
|
+
return `${subscriptionName}|${address}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const parseSubscriptionKey = (subscriptionKey) => {
|
|
15
|
+
const [subscriptionName, address] = subscriptionKey.split('|')
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
subscriptionName,
|
|
19
|
+
subscriptionAddress: address,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef subscribeWalletAddresses
|
|
25
|
+
* @property {string} network
|
|
26
|
+
* @property {string} address
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
class WsGateway extends EventEmitter {
|
|
30
|
+
#socket = null
|
|
31
|
+
// Dedup subscriptions to reduce workload on server and resubscribe after closing
|
|
32
|
+
#subscriptions = new Set()
|
|
33
|
+
#reconnectionTimeoutId = null
|
|
34
|
+
#defaultUri = 'wss://ws-gateway-clarity.a.exodus.io'
|
|
35
|
+
#uri = null
|
|
36
|
+
#logger = createConsoleLogger(`@exodus/ws-gateway`)
|
|
37
|
+
|
|
38
|
+
_handlers = {
|
|
39
|
+
message: ({ target, data }) => {
|
|
40
|
+
if (target !== this.#socket) return
|
|
41
|
+
const msg = JSON.parse(data)
|
|
42
|
+
|
|
43
|
+
if (Array.isArray(msg)) {
|
|
44
|
+
for (const item of msg) {
|
|
45
|
+
this.#handleMessage(item)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.#handleMessage(msg)
|
|
52
|
+
},
|
|
53
|
+
close: ({ target, code, reason }) => {
|
|
54
|
+
if (target !== this.#socket) return
|
|
55
|
+
this.#reconnect({ code, reason })
|
|
56
|
+
},
|
|
57
|
+
open: ({ target }) => {
|
|
58
|
+
if (target !== this.#socket) return
|
|
59
|
+
this.emit('opened')
|
|
60
|
+
},
|
|
61
|
+
error: ({ target }) => {
|
|
62
|
+
if (target !== this.#socket) return
|
|
63
|
+
this.#reconnect({ code: 0, reason: 'Error' })
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getSocket() {
|
|
68
|
+
return this.#socket
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getSubscriptions() {
|
|
72
|
+
return this.#subscriptions
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setServer(uri) {
|
|
76
|
+
this.#uri = uri || this.#uri || this.#defaultUri
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
start() {
|
|
80
|
+
if (this.#socket) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.#socket = new WebSocket(this.#uri)
|
|
85
|
+
|
|
86
|
+
this.#socket.addEventListener('message', this._handlers.message)
|
|
87
|
+
this.#socket.addEventListener('close', this._handlers.close)
|
|
88
|
+
this.#socket.addEventListener('open', this._handlers.open)
|
|
89
|
+
this.#socket.addEventListener('error', this._handlers.error)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#reconnect({ code, reason } = {}) {
|
|
93
|
+
this.#logger.warn(`Reconnect. Code ${code}, ${reason || 'Empty reason'}`)
|
|
94
|
+
|
|
95
|
+
this.#clearSocketListeners()
|
|
96
|
+
|
|
97
|
+
if (this.#socket.readyState === WebSocket.CLOSED) this.#socket = null
|
|
98
|
+
clearTimeout(this.#reconnectionTimeoutId)
|
|
99
|
+
this.#subscriptions.clear()
|
|
100
|
+
|
|
101
|
+
this.#reconnectionTimeoutId = setTimeout(() => {
|
|
102
|
+
this.start()
|
|
103
|
+
clearTimeout(this.#reconnectionTimeoutId)
|
|
104
|
+
}, 2500)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
unsubscribeWalletAddresses({ network, addresses = [] } = {}) {
|
|
108
|
+
assert(network, '"network" is required')
|
|
109
|
+
|
|
110
|
+
const unsubscribeName = createSubscriptionName(network)
|
|
111
|
+
|
|
112
|
+
const payload = []
|
|
113
|
+
|
|
114
|
+
const addressesSet = new Set(addresses)
|
|
115
|
+
|
|
116
|
+
for (const subscriptionKey of this.#subscriptions) {
|
|
117
|
+
const { subscriptionName, subscriptionAddress } = parseSubscriptionKey(subscriptionKey)
|
|
118
|
+
if (subscriptionName !== unsubscribeName) continue
|
|
119
|
+
|
|
120
|
+
// Unsubscribe whole network
|
|
121
|
+
if (addressesSet.size === 0) {
|
|
122
|
+
if (!payload.some((item) => item.subscription === subscriptionName)) {
|
|
123
|
+
payload.push({
|
|
124
|
+
subscription: subscriptionName,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.#subscriptions.delete(subscriptionKey)
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (addressesSet.has(subscriptionAddress)) {
|
|
133
|
+
payload.push({
|
|
134
|
+
subscription: subscriptionName,
|
|
135
|
+
entityId: subscriptionAddress,
|
|
136
|
+
})
|
|
137
|
+
this.#subscriptions.delete(subscriptionKey)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (payload.length === 0) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.#sendMessage({
|
|
146
|
+
eventName: 'unsubscribe',
|
|
147
|
+
payload,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {Array<subscribeWalletAddresses>} subscriptions
|
|
153
|
+
*/
|
|
154
|
+
subscribeWalletAddresses({ network, addresses }) {
|
|
155
|
+
const payload = []
|
|
156
|
+
|
|
157
|
+
for (const address of addresses) {
|
|
158
|
+
const subscriptionName = createSubscriptionName(network)
|
|
159
|
+
const subscriptionKey = createSubscriptionKey({ subscriptionName, address })
|
|
160
|
+
|
|
161
|
+
if (this.#subscriptions.has(subscriptionKey)) {
|
|
162
|
+
continue
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
payload.push({
|
|
166
|
+
subscription: subscriptionName,
|
|
167
|
+
entityId: address,
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (payload.length === 0) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.#sendMessage({
|
|
176
|
+
eventName: 'subscribe',
|
|
177
|
+
payload,
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#sendMessage(message) {
|
|
182
|
+
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.#socket.send(JSON.stringify(message))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#handleMessage(message) {
|
|
190
|
+
switch (message.type) {
|
|
191
|
+
case 'new_transactions': {
|
|
192
|
+
const { subscription, entityId, payload } = message
|
|
193
|
+
const network = subscription.split('/')[2]
|
|
194
|
+
|
|
195
|
+
for (const transaction of payload.transactions) {
|
|
196
|
+
this.emit(`${network}:new_transaction`, {
|
|
197
|
+
address: entityId,
|
|
198
|
+
transaction,
|
|
199
|
+
cursor: payload.cursor,
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
case 'connection_ack': {
|
|
207
|
+
this.emit('connected', message)
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
case 'subscribe_ack': {
|
|
212
|
+
const { subscription, entityId } = message
|
|
213
|
+
|
|
214
|
+
const subscriptionKey = createSubscriptionKey({
|
|
215
|
+
subscriptionName: subscription,
|
|
216
|
+
address: entityId,
|
|
217
|
+
})
|
|
218
|
+
this.#subscriptions.add(subscriptionKey)
|
|
219
|
+
|
|
220
|
+
this.emit('subscribed', message)
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case 'error': {
|
|
225
|
+
this.#logger.error('Error from server', message)
|
|
226
|
+
this.emit('error', message)
|
|
227
|
+
break
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
default: {
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#clearSocketListeners() {
|
|
237
|
+
if (!this.#socket) return
|
|
238
|
+
|
|
239
|
+
this.#socket.removeEventListener('message', this._handlers.message)
|
|
240
|
+
this.#socket.removeEventListener('close', this._handlers.close)
|
|
241
|
+
this.#socket.removeEventListener('open', this._handlers.open)
|
|
242
|
+
this.#socket.removeEventListener('error', this._handlers.error)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
dispose(network, addresses) {
|
|
246
|
+
this.unsubscribeWalletAddresses({ network, addresses })
|
|
247
|
+
|
|
248
|
+
if (this.#subscriptions.size > 0) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.#clearSocketListeners()
|
|
253
|
+
|
|
254
|
+
clearTimeout(this.#reconnectionTimeoutId)
|
|
255
|
+
|
|
256
|
+
if (this.#socket && [WebSocket.OPEN, WebSocket.CONNECTING].includes(this.#socket.readyState)) {
|
|
257
|
+
this.#socket.close()
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this.#socket = null
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const wsGateway = new WsGateway()
|
|
265
|
+
const createWsGateway = () => wsGateway
|
|
266
|
+
|
|
267
|
+
export { createWsGateway, WsGateway }
|
package/src/tx-create.js
CHANGED
|
@@ -6,7 +6,7 @@ import * as ErrorWrapper from './error-wrapper.js'
|
|
|
6
6
|
import { isContractAddressCached } from './eth-like-util.js'
|
|
7
7
|
import { ensureSaneEip1559GasPriceForTipGasPrice } from './fee-utils.js'
|
|
8
8
|
import { ARBITRARY_ADDRESS, fetchGasLimit, resolveDefaultTxInput } from './gas-estimation.js'
|
|
9
|
-
import { getFeeFactoryGasPrices } from './get-fee.js'
|
|
9
|
+
import { getExtraFeeData, getFeeFactoryGasPrices } from './get-fee.js'
|
|
10
10
|
import { getNftArguments } from './nft-utils.js'
|
|
11
11
|
|
|
12
12
|
async function createUnsignedTxWithFees({
|
|
@@ -66,7 +66,7 @@ async function createUnsignedTxWithFees({
|
|
|
66
66
|
: asset.baseAsset.currency.ZERO
|
|
67
67
|
|
|
68
68
|
const fee = baseFee.add(l1DataFee)
|
|
69
|
-
|
|
69
|
+
const extraFeeData = getExtraFeeData({ asset, amount: coinAmount })
|
|
70
70
|
const unsignedTx = {
|
|
71
71
|
txData: { transactionBuffer, chainId },
|
|
72
72
|
txMeta: {
|
|
@@ -81,6 +81,8 @@ async function createUnsignedTxWithFees({
|
|
|
81
81
|
}
|
|
82
82
|
return {
|
|
83
83
|
unsignedTx,
|
|
84
|
+
fee,
|
|
85
|
+
extraFeeData,
|
|
84
86
|
// exhcange compatibility until the use usignedTx, remove me!
|
|
85
87
|
gasPrice,
|
|
86
88
|
tipGasPrice,
|
|
@@ -195,7 +197,6 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
|
|
|
195
197
|
return async ({
|
|
196
198
|
asset,
|
|
197
199
|
walletAccount,
|
|
198
|
-
feeData,
|
|
199
200
|
nft, // when sending nfts
|
|
200
201
|
fromAddress: providedFromAddress, // wallet from address
|
|
201
202
|
toAddress: providedToAddress, // user's to address, not the token or the dex contract
|
|
@@ -213,8 +214,16 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
|
|
|
213
214
|
keepTxInput, // @deprecated this flag is used by swaps when swapping a token via DEX. The asset is token but the tx TO address is not the token address. Update swap to use `contractAddress`
|
|
214
215
|
}) => {
|
|
215
216
|
assert(asset, 'asset is required')
|
|
216
|
-
assert(
|
|
217
|
-
|
|
217
|
+
assert(walletAccount, 'walletAccount is required')
|
|
218
|
+
|
|
219
|
+
const feeData = await assetClientInterface.getFeeConfig({ assetName: asset.baseAsset.name })
|
|
220
|
+
|
|
221
|
+
const fromAddress =
|
|
222
|
+
providedFromAddress ??
|
|
223
|
+
(await assetClientInterface.getReceiveAddress({
|
|
224
|
+
assetName: asset.baseAsset.name,
|
|
225
|
+
walletAccount,
|
|
226
|
+
}))
|
|
218
227
|
|
|
219
228
|
const baseAssetTxLog = await assetClientInterface.getTxLog({
|
|
220
229
|
assetName: asset.baseAsset.name,
|
|
@@ -320,7 +329,7 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
|
|
|
320
329
|
(await fetchGasLimit({
|
|
321
330
|
asset,
|
|
322
331
|
feeData,
|
|
323
|
-
fromAddress
|
|
332
|
+
fromAddress,
|
|
324
333
|
toAddress: providedToAddress,
|
|
325
334
|
txInput: providedTxInput,
|
|
326
335
|
contractAddress: txToAddress,
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { BaseMonitor } from '@exodus/asset-lib'
|
|
2
|
+
import { getAssetAddresses } from '@exodus/ethereum-lib'
|
|
3
|
+
import lodash from 'lodash'
|
|
4
|
+
|
|
5
|
+
import { createWsGateway } from '../exodus-eth-server/ws-gateway.js'
|
|
6
|
+
import { executeEthLikeFeeMonitorUpdate } from '../fee-utils.js'
|
|
7
|
+
import { fromHexToString } from '../number-utils.js'
|
|
8
|
+
import { filterEffects, getLogItemsFromServerTx } from './clarity-utils/index.js'
|
|
9
|
+
import {
|
|
10
|
+
checkPendingTransactions,
|
|
11
|
+
excludeUnchangedTokenBalances,
|
|
12
|
+
getAllLogItemsByAsset,
|
|
13
|
+
getDeriveDataNeededForTick,
|
|
14
|
+
getDeriveTransactionsToCheck,
|
|
15
|
+
} from './monitor-utils/index.js'
|
|
16
|
+
|
|
17
|
+
const { isEmpty } = lodash
|
|
18
|
+
|
|
19
|
+
export class ClarityMonitorV2 extends BaseMonitor {
|
|
20
|
+
#wsClient = null
|
|
21
|
+
#walletAccountByAddress = new Map()
|
|
22
|
+
#walletAccountInfo = new Map()
|
|
23
|
+
#rpcBalanceAssetNames = []
|
|
24
|
+
constructor({
|
|
25
|
+
server,
|
|
26
|
+
wsGatewayClient = createWsGateway(),
|
|
27
|
+
rpcBalanceAssetNames,
|
|
28
|
+
config,
|
|
29
|
+
...args
|
|
30
|
+
} = {}) {
|
|
31
|
+
super(args)
|
|
32
|
+
this.config = { GAS_PRICE_FROM_WEBSOCKET: true, ...config }
|
|
33
|
+
this.server = server
|
|
34
|
+
this.#wsClient = wsGatewayClient
|
|
35
|
+
this.#rpcBalanceAssetNames = rpcBalanceAssetNames
|
|
36
|
+
this.getAllLogItemsByAsset = getAllLogItemsByAsset
|
|
37
|
+
this.deriveDataNeededForTick = getDeriveDataNeededForTick(this.aci)
|
|
38
|
+
this.deriveTransactionsToCheck = getDeriveTransactionsToCheck({
|
|
39
|
+
getTxLog: (...args) => this.aci.getTxLog(...args),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
this.addHook('before-start', (...args) => this.beforeStart(...args))
|
|
43
|
+
this.addHook('after-stop', (...args) => this.afterStop(...args))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setServer(config) {
|
|
47
|
+
const uri = config?.server || this.server.defaultUri
|
|
48
|
+
|
|
49
|
+
this.#wsClient.setServer(config.wsGatewayUrl?.v1)
|
|
50
|
+
|
|
51
|
+
this.#wsClient.on('connected', () => this.subscribeAllWalletAccounts())
|
|
52
|
+
this.#wsClient.start()
|
|
53
|
+
|
|
54
|
+
if (uri === this.server.uri) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.server.setURI(uri)
|
|
59
|
+
if (this.config.GAS_PRICE_FROM_WEBSOCKET) {
|
|
60
|
+
this.server.connectFee()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async deriveData({ assetName, walletAccount, tokens }) {
|
|
65
|
+
const { ourWalletAddress, currentAccountState } = await this.deriveDataNeededForTick({
|
|
66
|
+
assetName,
|
|
67
|
+
walletAccount,
|
|
68
|
+
})
|
|
69
|
+
const transactionsToCheck = await this.deriveTransactionsToCheck({
|
|
70
|
+
assetName,
|
|
71
|
+
walletAccount,
|
|
72
|
+
tokens,
|
|
73
|
+
ourWalletAddress,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
ourWalletAddress,
|
|
78
|
+
currentAccountState,
|
|
79
|
+
...transactionsToCheck,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line no-undef
|
|
84
|
+
async checkPendingTransactions(params) {
|
|
85
|
+
const { pendingTransactionsToCheck, pendingTransactionsGroupedByAddressAndNonce } =
|
|
86
|
+
checkPendingTransactions(params)
|
|
87
|
+
const txsToRemove = []
|
|
88
|
+
const { walletAccount } = params
|
|
89
|
+
|
|
90
|
+
const updateTx = (tx, asset, { error, remove }) => {
|
|
91
|
+
if (remove) {
|
|
92
|
+
txsToRemove.push({ tx, assetSource: { asset, walletAccount } })
|
|
93
|
+
} else {
|
|
94
|
+
params.logItemsByAsset[asset].push({
|
|
95
|
+
...tx,
|
|
96
|
+
dropped: true,
|
|
97
|
+
error,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// in case this is an ETH fee tx that has associated ERC20 send txs
|
|
102
|
+
const promises = tx.tokens.map(async (assetName) => {
|
|
103
|
+
const tokenTxSet = await this.aci.getTxLog({ assetName, walletAccount })
|
|
104
|
+
if (remove) {
|
|
105
|
+
txsToRemove.push({
|
|
106
|
+
tx: tokenTxSet.get(tx.txId),
|
|
107
|
+
assetSource: { asset: assetName, walletAccount },
|
|
108
|
+
})
|
|
109
|
+
} else if (tokenTxSet && tokenTxSet.has(tx.txId)) {
|
|
110
|
+
params.logItemsByAsset[assetName].push({
|
|
111
|
+
...tokenTxSet.get(tx.txId),
|
|
112
|
+
error,
|
|
113
|
+
dropped: true,
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
return Promise.all(promises)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (const { tx, assetName, replaced = false } of Object.values(
|
|
121
|
+
pendingTransactionsGroupedByAddressAndNonce
|
|
122
|
+
)) {
|
|
123
|
+
if (replaced) {
|
|
124
|
+
await updateTx(tx, assetName, { remove: true })
|
|
125
|
+
delete pendingTransactionsToCheck[tx.txId]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const { tx, assetName } of Object.values(pendingTransactionsToCheck)) {
|
|
130
|
+
if (params.refresh) await updateTx(tx, assetName, { remove: true })
|
|
131
|
+
else await updateTx(tx, assetName, { error: 'Dropped' })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { txsToRemove }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async tick({ walletAccount, refresh }) {
|
|
138
|
+
await this.subscribeWalletAddresses(walletAccount)
|
|
139
|
+
|
|
140
|
+
const walletAccountInfo = this.#walletAccountInfo.get(walletAccount)
|
|
141
|
+
|
|
142
|
+
if (!walletAccountInfo) {
|
|
143
|
+
return this.logger.warn('walletAccountInfo is empty', { walletAccount })
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { derivedData, tokensByAddress, assets, tokens, assetName } = walletAccountInfo
|
|
147
|
+
|
|
148
|
+
const response = await this.getHistoryFromServer({ walletAccount, derivedData, refresh })
|
|
149
|
+
const allTxs = [...response.transactions.pending, ...response.transactions.confirmed]
|
|
150
|
+
const cursor = response.cursor
|
|
151
|
+
|
|
152
|
+
await this.processAndFillTransactionsToState({
|
|
153
|
+
allTxs,
|
|
154
|
+
derivedData,
|
|
155
|
+
tokensByAddress,
|
|
156
|
+
assets,
|
|
157
|
+
tokens,
|
|
158
|
+
assetName,
|
|
159
|
+
walletAccount,
|
|
160
|
+
refresh,
|
|
161
|
+
cursor,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async processAndFillTransactionsToState({
|
|
166
|
+
allTxs,
|
|
167
|
+
derivedData,
|
|
168
|
+
tokensByAddress,
|
|
169
|
+
assets,
|
|
170
|
+
tokens,
|
|
171
|
+
assetName,
|
|
172
|
+
walletAccount,
|
|
173
|
+
refresh,
|
|
174
|
+
cursor,
|
|
175
|
+
}) {
|
|
176
|
+
const hasNewTxs = allTxs.length > 0
|
|
177
|
+
|
|
178
|
+
const logItemsByAsset = this.getAllLogItemsByAsset({
|
|
179
|
+
getLogItemsFromServerTx,
|
|
180
|
+
ourWalletAddress: derivedData.ourWalletAddress,
|
|
181
|
+
allTransactionsFromServer: allTxs,
|
|
182
|
+
asset: this.asset,
|
|
183
|
+
tokensByAddress,
|
|
184
|
+
assets,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const { txsToRemove } = await this.checkPendingTransactions({
|
|
188
|
+
txlist: allTxs,
|
|
189
|
+
walletAccount,
|
|
190
|
+
refresh,
|
|
191
|
+
logItemsByAsset,
|
|
192
|
+
asset: this.asset,
|
|
193
|
+
...derivedData,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const accountState = await this.getNewAccountState({
|
|
197
|
+
tokens,
|
|
198
|
+
currentTokenBalances: derivedData.currentAccountState?.tokenBalances,
|
|
199
|
+
ourWalletAddress: derivedData.ourWalletAddress,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const batch = this.aci.createOperationsBatch()
|
|
203
|
+
|
|
204
|
+
this.aci.removeTxLogBatch({
|
|
205
|
+
assetName,
|
|
206
|
+
walletAccount,
|
|
207
|
+
txs: txsToRemove,
|
|
208
|
+
batch,
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
for (const [assetName, txs] of Object.entries(logItemsByAsset)) {
|
|
212
|
+
this.aci.updateTxLogAndNotifyBatch({
|
|
213
|
+
assetName,
|
|
214
|
+
walletAccount,
|
|
215
|
+
txs,
|
|
216
|
+
refresh,
|
|
217
|
+
batch,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const newData = { ...accountState }
|
|
222
|
+
|
|
223
|
+
if (cursor) {
|
|
224
|
+
newData.clarityCursor = cursor
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.aci.updateAccountStateBatch({
|
|
228
|
+
assetName,
|
|
229
|
+
walletAccount,
|
|
230
|
+
accountState,
|
|
231
|
+
newData,
|
|
232
|
+
batch,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
await this.aci.executeOperationsBatch(batch)
|
|
236
|
+
|
|
237
|
+
if (refresh || hasNewTxs) {
|
|
238
|
+
const unknownTokenAddresses = this.getUnknownTokenAddresses({
|
|
239
|
+
transactions: allTxs,
|
|
240
|
+
tokensByAddress,
|
|
241
|
+
})
|
|
242
|
+
if (unknownTokenAddresses.length > 0) {
|
|
243
|
+
this.emit('unknown-tokens', unknownTokenAddresses)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async addSingleTx({ tx, address, cursor }) {
|
|
249
|
+
const walletAccounts = this.#walletAccountByAddress.get(address)
|
|
250
|
+
|
|
251
|
+
if (!walletAccounts || walletAccounts.length === 0) {
|
|
252
|
+
return
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
for (const walletAccount of walletAccounts) {
|
|
256
|
+
const walletAccountInfo = this.#walletAccountInfo.get(walletAccount)
|
|
257
|
+
|
|
258
|
+
if (!walletAccountInfo) {
|
|
259
|
+
continue
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const { derivedData, tokensByAddress, assets, tokens, assetName } = walletAccountInfo
|
|
263
|
+
|
|
264
|
+
await this.processAndFillTransactionsToState({
|
|
265
|
+
allTxs: [tx],
|
|
266
|
+
derivedData,
|
|
267
|
+
tokensByAddress,
|
|
268
|
+
assets,
|
|
269
|
+
tokens,
|
|
270
|
+
assetName,
|
|
271
|
+
walletAccount,
|
|
272
|
+
refresh: false,
|
|
273
|
+
cursor,
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async getNewAccountState({ tokens, currentTokenBalances, ourWalletAddress }) {
|
|
279
|
+
const asset = this.asset
|
|
280
|
+
const newAccountState = Object.create(null)
|
|
281
|
+
const balances = await this.getBalances({ tokens, ourWalletAddress })
|
|
282
|
+
if (this.#rpcBalanceAssetNames.includes(asset.name)) {
|
|
283
|
+
const balance = balances[asset.name]
|
|
284
|
+
newAccountState.balance = asset.currency.baseUnit(balance)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const tokenBalancePairs = Object.entries(balances).filter((entry) => entry[0] !== asset.name)
|
|
288
|
+
const tokenBalanceEntries = tokenBalancePairs
|
|
289
|
+
.map((pair) => {
|
|
290
|
+
const token = tokens.find((token) => token.name === pair[0])
|
|
291
|
+
const value = token.currency.baseUnit(pair[1] || 0)
|
|
292
|
+
return [token.name, value]
|
|
293
|
+
})
|
|
294
|
+
.filter(Boolean)
|
|
295
|
+
|
|
296
|
+
const tokenBalances = excludeUnchangedTokenBalances(currentTokenBalances, tokenBalanceEntries)
|
|
297
|
+
if (!isEmpty(tokenBalances)) newAccountState.tokenBalances = tokenBalances
|
|
298
|
+
return newAccountState
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async getReceiveAddressesByWalletAccount() {
|
|
302
|
+
const walletAccounts = await this.aci.getWalletAccounts({ assetName: this.asset.name })
|
|
303
|
+
const addressesByAccount = Object.create(null)
|
|
304
|
+
for (const walletAccount of walletAccounts) {
|
|
305
|
+
addressesByAccount[walletAccount] = await this.aci.getReceiveAddresses({
|
|
306
|
+
assetName: this.asset.name,
|
|
307
|
+
walletAccount,
|
|
308
|
+
useCache: true,
|
|
309
|
+
})
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return addressesByAccount
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async fillAssetsTokensAndData({ walletAccount }) {
|
|
316
|
+
const assetName = this.asset.name
|
|
317
|
+
const assets = await this.aci.getAssetsForNetwork({ baseAssetName: assetName })
|
|
318
|
+
const tokens = Object.values(assets).filter((asset) => assetName !== asset.name)
|
|
319
|
+
|
|
320
|
+
const tokensByAddress = tokens.reduce((map, token) => {
|
|
321
|
+
const addresses = getAssetAddresses(token)
|
|
322
|
+
for (const address of addresses) map.set(address.toLowerCase(), token)
|
|
323
|
+
return map
|
|
324
|
+
}, new Map())
|
|
325
|
+
|
|
326
|
+
const derivedData = await this.deriveData({ assetName, walletAccount, tokens })
|
|
327
|
+
|
|
328
|
+
this.#walletAccountInfo.set(walletAccount, {
|
|
329
|
+
assets,
|
|
330
|
+
tokens,
|
|
331
|
+
tokensByAddress,
|
|
332
|
+
derivedData,
|
|
333
|
+
assetName,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async subscribeAllWalletAccounts() {
|
|
338
|
+
const addressesByWalletAccount = await this.getReceiveAddressesByWalletAccount()
|
|
339
|
+
const entriesAddressesByWalletAccount = Object.entries(addressesByWalletAccount)
|
|
340
|
+
|
|
341
|
+
for (const [walletAccount] of entriesAddressesByWalletAccount) {
|
|
342
|
+
await this.subscribeWalletAddresses(walletAccount)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async subscribeWalletAddresses(walletAccount) {
|
|
347
|
+
const addressesByWalletAccount = await this.aci.getReceiveAddresses({
|
|
348
|
+
assetName: this.asset.name,
|
|
349
|
+
walletAccount,
|
|
350
|
+
useCache: true,
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const address = addressesByWalletAccount[0].toLowerCase() // Only check m/0/0
|
|
354
|
+
await this.fillAssetsTokensAndData({ walletAccount })
|
|
355
|
+
|
|
356
|
+
if (!this.#walletAccountByAddress.has(address)) {
|
|
357
|
+
this.#walletAccountByAddress.set(address, [])
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const walletAccounts = this.#walletAccountByAddress.get(address)
|
|
361
|
+
|
|
362
|
+
if (!walletAccounts.includes(walletAccount)) {
|
|
363
|
+
walletAccounts.push(walletAccount)
|
|
364
|
+
this.#walletAccountByAddress.set(address, walletAccounts)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
this.server.connectTransactions({ walletAccount, address })
|
|
368
|
+
|
|
369
|
+
this.#wsClient.subscribeWalletAddresses({
|
|
370
|
+
network: this.asset.name,
|
|
371
|
+
addresses: [address],
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async getBalances({ tokens, ourWalletAddress }) {
|
|
376
|
+
const batch = Object.create(null)
|
|
377
|
+
if (this.#rpcBalanceAssetNames.includes(this.asset.name)) {
|
|
378
|
+
const request = this.server.getBalanceRequest(ourWalletAddress)
|
|
379
|
+
batch[this.asset.name] = request
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (const token of tokens) {
|
|
383
|
+
if (this.#rpcBalanceAssetNames.includes(token.name) && token.contract.address) {
|
|
384
|
+
const request = this.server.balanceOfRequest(ourWalletAddress, token.contract.address)
|
|
385
|
+
batch[token.name] = request
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const pairs = Object.entries(batch)
|
|
390
|
+
if (pairs.length === 0) {
|
|
391
|
+
return {}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const requests = pairs.map((pair) => pair[1])
|
|
395
|
+
const responses = await this.server.sendBatchRequest(requests)
|
|
396
|
+
const entries = pairs.map((pair, idx) => {
|
|
397
|
+
const balanceHex = responses[idx]
|
|
398
|
+
const name = pair[0]
|
|
399
|
+
const balance = fromHexToString(balanceHex)
|
|
400
|
+
return [name, balance]
|
|
401
|
+
})
|
|
402
|
+
return Object.fromEntries(entries)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
getUnknownTokenAddresses({ transactions, tokensByAddress }) {
|
|
406
|
+
const set = transactions.reduce((acc, txn) => {
|
|
407
|
+
const transfers = filterEffects(txn.effects, 'erc20') || []
|
|
408
|
+
transfers.forEach((transfer) => {
|
|
409
|
+
const addr = transfer.address.toLowerCase()
|
|
410
|
+
if (!tokensByAddress.has(addr)) {
|
|
411
|
+
acc.add(addr)
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
return acc
|
|
415
|
+
}, new Set())
|
|
416
|
+
return [...set]
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// NOTE: Here, fetchedGasPrices is the result of a call to `ClarityMonitor.getFee()`.
|
|
420
|
+
async updateGasPrice(fetchedGasPrices) {
|
|
421
|
+
try {
|
|
422
|
+
await executeEthLikeFeeMonitorUpdate({
|
|
423
|
+
assetClientInterface: this.aci,
|
|
424
|
+
feeAsset: this.asset,
|
|
425
|
+
fetchedGasPrices,
|
|
426
|
+
})
|
|
427
|
+
} catch (e) {
|
|
428
|
+
this.logger.warn('error updating gasPrice', e)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async onFeeUpdated(fee) {
|
|
433
|
+
return this.updateGasPrice(fee)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async beforeStart() {
|
|
437
|
+
this.listenToServerEvents()
|
|
438
|
+
if (this.config.GAS_PRICE_FROM_WEBSOCKET) {
|
|
439
|
+
this.server.connectFee()
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async afterStop() {
|
|
444
|
+
this.server.dispose()
|
|
445
|
+
this.#wsClient.dispose(this.asset.name)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async getHistoryFromServer({ walletAccount, derivedData, refresh }) {
|
|
449
|
+
const address = derivedData.ourWalletAddress
|
|
450
|
+
const currentCursor = derivedData.currentAccountState?.clarityCursor
|
|
451
|
+
const cursor = currentCursor && !refresh ? currentCursor : null
|
|
452
|
+
return this.server.getAllTransactions({ walletAccount, address, cursor })
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
listenToServerEvents() {
|
|
456
|
+
this.server.on('feeUpdated', (...args) => this.onFeeUpdated(...args))
|
|
457
|
+
this.#wsClient.on(
|
|
458
|
+
`${this.asset.name}:new_transaction`,
|
|
459
|
+
async ({ transaction, address, cursor }) =>
|
|
460
|
+
this.addSingleTx({
|
|
461
|
+
tx: transaction,
|
|
462
|
+
address,
|
|
463
|
+
cursor,
|
|
464
|
+
})
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
}
|
package/src/tx-send/tx-send.js
CHANGED
|
@@ -33,24 +33,9 @@ const txSendFactory = ({ assetClientInterface, createTx }) => {
|
|
|
33
33
|
return { unsignedTx: providedUnsignedTx }
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const feeData =
|
|
37
|
-
legacyParams.feeData ??
|
|
38
|
-
(await assetClientInterface.getFeeData({
|
|
39
|
-
assetName: baseAsset.name,
|
|
40
|
-
}))
|
|
41
|
-
|
|
42
|
-
const fromAddress =
|
|
43
|
-
legacyParams.fromAddress ??
|
|
44
|
-
(await assetClientInterface.getReceiveAddress({
|
|
45
|
-
assetName: baseAsset.name,
|
|
46
|
-
walletAccount,
|
|
47
|
-
}))
|
|
48
|
-
|
|
49
36
|
return createTx({
|
|
50
37
|
asset,
|
|
51
38
|
walletAccount,
|
|
52
|
-
feeData,
|
|
53
|
-
fromAddress,
|
|
54
39
|
toAddress: legacyParams.address,
|
|
55
40
|
...legacyParams,
|
|
56
41
|
...legacyParams.options,
|
package/src/get-fee-async.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import assert from 'minimalistic-assert'
|
|
2
|
-
|
|
3
|
-
import { getExtraFeeData } from './get-fee.js'
|
|
4
|
-
|
|
5
|
-
const getFeeAsyncFactory = ({ assetClientInterface, createTx }) => {
|
|
6
|
-
assert(assetClientInterface, 'assetClientInterface is required')
|
|
7
|
-
assert(createTx, 'createTx is required')
|
|
8
|
-
|
|
9
|
-
return async (params) => {
|
|
10
|
-
const { asset } = params
|
|
11
|
-
const { unsignedTx, gasPrice, tipGasPrice, gasLimit } = params.unsignedTx
|
|
12
|
-
? params
|
|
13
|
-
: await createTx(params)
|
|
14
|
-
const fee = asset.feeAsset.currency.parse(unsignedTx.txMeta.fee)
|
|
15
|
-
const coinAmount = asset.currency.parse(unsignedTx.txMeta.amount)
|
|
16
|
-
const extraFeeData = getExtraFeeData({ asset, amount: coinAmount })
|
|
17
|
-
return {
|
|
18
|
-
fee,
|
|
19
|
-
extraFeeData,
|
|
20
|
-
unsignedTx,
|
|
21
|
-
// deprecated, exchanges uses these params to recreate the tx during send. It should just use unsignedTx...
|
|
22
|
-
gasPrice,
|
|
23
|
-
tipGasPrice,
|
|
24
|
-
gasLimit,
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export default getFeeAsyncFactory
|