@exodus/ethereum-api 8.34.5 → 8.34.7
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 +2 -2
- package/src/create-asset.js +1 -2
- package/src/custom-fees.js +40 -10
- package/src/etherscan/ws.js +1 -2
- package/src/exodus-eth-server/ws.js +1 -2
- package/src/fee-utils.js +17 -3
- package/src/gas-estimation.js +25 -6
- package/src/get-fee-async.js +0 -14
- package/src/get-fee.js +29 -35
- package/src/index.js +2 -0
- package/src/staking/ethereum/api.js +1 -0
- package/src/staking/ethereum/service.js +116 -92
- package/src/staking/matic/matic-staking.md +53 -0
- package/src/staking/matic/service.js +189 -102
- package/src/staking/utils/index.js +26 -60
- package/src/tx-send/get-fee-info.js +34 -9
- package/src/tx-send/nonce-utils.js +2 -1
- package/src/tx-send/tx-send.js +80 -32
- package/src/websocket/index.android.js +0 -2
- package/src/websocket/index.ios.js +0 -2
- package/src/websocket/index.js +0 -2
- package/src/websocket/websocket.cjs +0 -21
package/src/tx-send/tx-send.js
CHANGED
|
@@ -29,7 +29,13 @@ export const HACK_maybeRefineSendAllAmount = async ({
|
|
|
29
29
|
gasPrice,
|
|
30
30
|
}) => {
|
|
31
31
|
try {
|
|
32
|
-
const { name: assetName } = asset
|
|
32
|
+
const { name: assetName, estimateL1DataFee } = asset
|
|
33
|
+
|
|
34
|
+
// HACK: For the interim, we won't attempt to
|
|
35
|
+
// reconcile transaction dust on L2s due
|
|
36
|
+
// to the nondeterminism about the calldata
|
|
37
|
+
// fee.
|
|
38
|
+
if (typeof estimateL1DataFee === 'function') return null
|
|
33
39
|
|
|
34
40
|
const [txLog, accountState] = await Promise.all([
|
|
35
41
|
assetClientInterface.getTxLog({
|
|
@@ -64,7 +70,7 @@ export const HACK_maybeRefineSendAllAmount = async ({
|
|
|
64
70
|
const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
65
71
|
assert(assetClientInterface, 'assetClientInterface is required')
|
|
66
72
|
assert(createUnsignedTx, 'createUnsignedTx is required')
|
|
67
|
-
return async ({ asset, walletAccount, address, amount, options = {} }) => {
|
|
73
|
+
return async ({ asset, walletAccount, address, amount, feeData: maybeFeeData, options = {} }) => {
|
|
68
74
|
const {
|
|
69
75
|
nft,
|
|
70
76
|
bumpTxId,
|
|
@@ -87,13 +93,18 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
87
93
|
|
|
88
94
|
const assets = await assetClientInterface.getAssetsForNetwork({ baseAssetName: baseAsset.name })
|
|
89
95
|
|
|
96
|
+
const feeData =
|
|
97
|
+
maybeFeeData ||
|
|
98
|
+
(await assetClientInterface.getFeeData({
|
|
99
|
+
assetName: baseAsset.name,
|
|
100
|
+
}))
|
|
101
|
+
|
|
102
|
+
const { eip1559Enabled } = feeData
|
|
103
|
+
|
|
90
104
|
const fromAddress = await assetClientInterface.getReceiveAddress({
|
|
91
105
|
assetName: baseAsset.name,
|
|
92
106
|
walletAccount,
|
|
93
107
|
})
|
|
94
|
-
const feeData = await assetClientInterface.getFeeData({
|
|
95
|
-
assetName: baseAsset.name,
|
|
96
|
-
})
|
|
97
108
|
|
|
98
109
|
let contractAddress
|
|
99
110
|
if (nft) {
|
|
@@ -106,8 +117,6 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
106
117
|
|
|
107
118
|
let bumpNonce
|
|
108
119
|
|
|
109
|
-
let eip1559Enabled = feeData.eip1559Enabled
|
|
110
|
-
|
|
111
120
|
const baseAssetTxLog = await assetClientInterface.getTxLog({
|
|
112
121
|
assetName: baseAsset.name,
|
|
113
122
|
walletAccount,
|
|
@@ -117,36 +126,47 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
117
126
|
let replacedTx, replacedTokenTx
|
|
118
127
|
if (bumpTxId) {
|
|
119
128
|
replacedTx = baseAssetTxLog.get(bumpTxId)
|
|
129
|
+
|
|
120
130
|
if (!replacedTx || !replacedTx.pending) {
|
|
121
131
|
throw new Error(`Cannot bump transaction ${bumpTxId}: not found or confirmed`)
|
|
122
132
|
}
|
|
123
133
|
|
|
124
134
|
if (replacedTx.tokens.length > 0) {
|
|
135
|
+
const [tokenAssetName] = replacedTx.tokens
|
|
125
136
|
const tokenTxSet = await assetClientInterface.getTxLog({
|
|
126
|
-
assetName:
|
|
137
|
+
assetName: tokenAssetName,
|
|
127
138
|
walletAccount,
|
|
128
139
|
})
|
|
129
140
|
replacedTokenTx = tokenTxSet.get(bumpTxId)
|
|
130
141
|
|
|
131
142
|
if (replacedTokenTx) {
|
|
132
|
-
asset
|
|
143
|
+
// Attempt to overwrite the asset to reflect the fact that
|
|
144
|
+
// we're performing a token transaction.
|
|
145
|
+
asset = assets[tokenAssetName]
|
|
146
|
+
if (!asset) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`unable to find ${tokenAssetName} during token bump transaction: asset was not available in assetsForNetwork`
|
|
149
|
+
)
|
|
150
|
+
}
|
|
133
151
|
}
|
|
152
|
+
|
|
153
|
+
// TODO: Should we `throw` if we can't find the asset?
|
|
134
154
|
}
|
|
135
155
|
|
|
136
156
|
address = (replacedTokenTx || replacedTx).to
|
|
137
157
|
amount = (replacedTokenTx || replacedTx).coinAmount.negate()
|
|
138
158
|
feeOpts.gasLimit = replacedTx.data.gasLimit
|
|
139
159
|
|
|
140
|
-
const { gasPrice: currentGasPrice } = feeData
|
|
160
|
+
const { gasPrice: currentGasPrice, baseFeePerGas: currentBaseFee } = feeData
|
|
141
161
|
const { bumpedGasPrice, bumpedTipGasPrice } = calculateBumpedGasPrice({
|
|
142
162
|
baseAsset,
|
|
143
163
|
tx: replacedTx,
|
|
144
164
|
currentGasPrice,
|
|
165
|
+
currentBaseFee,
|
|
145
166
|
eip1559Enabled,
|
|
146
167
|
})
|
|
147
168
|
feeOpts.gasPrice = bumpedGasPrice
|
|
148
169
|
feeOpts.tipGasPrice = bumpedTipGasPrice
|
|
149
|
-
eip1559Enabled = feeData.eip1559Enabled && feeOpts.tipGasPrice
|
|
150
170
|
bumpNonce = replacedTx.data.nonce
|
|
151
171
|
txInput = replacedTokenTx ? null : replacedTx.data.data || '0x'
|
|
152
172
|
if (bumpNonce === undefined) {
|
|
@@ -154,9 +174,40 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
154
174
|
}
|
|
155
175
|
}
|
|
156
176
|
|
|
177
|
+
// If we have evaluated a bump transaction and the `providedNonce` differs
|
|
178
|
+
// from the `bumpNonce`, we've encountered a conflict and cannot respect
|
|
179
|
+
// the caller's request.
|
|
180
|
+
if (
|
|
181
|
+
typeof bumpNonce === 'number' &&
|
|
182
|
+
typeof providedNonce === 'number' &&
|
|
183
|
+
bumpNonce !== providedNonce
|
|
184
|
+
)
|
|
185
|
+
throw new ErrorWrapper.EthLikeError({
|
|
186
|
+
message: new Error('incorrect nonce for replacement transaction'),
|
|
187
|
+
reason: ErrorWrapper.reasons.bumpTxFailed,
|
|
188
|
+
hint: 'providedNonce',
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// Choose a nonce that compensates for transctions which are currently
|
|
192
|
+
// in pending; for example, when we send transactions at low gas which
|
|
193
|
+
// will be stored by `geth` for execution at a later point in time.
|
|
194
|
+
//
|
|
195
|
+
// When we are not intentionally bumping a transaction, users are
|
|
196
|
+
// appending a new transaction to the chain - therefore we should
|
|
197
|
+
// be mindful of nonces belonging to us which are currently pending.
|
|
157
198
|
const resolvedNonce =
|
|
199
|
+
providedNonce ??
|
|
158
200
|
bumpNonce ??
|
|
159
|
-
(await resolveNonce({
|
|
201
|
+
(await resolveNonce({
|
|
202
|
+
asset,
|
|
203
|
+
fromAddress,
|
|
204
|
+
txLog: baseAssetTxLog,
|
|
205
|
+
// For assets where we'll fall back to querying the coin node, we
|
|
206
|
+
// search for pending transactions. For base assets with history,
|
|
207
|
+
// we'll fall back to the `TxLog` since this also has a knowledge
|
|
208
|
+
// of which transactions are currently in pending.
|
|
209
|
+
tag: 'pending',
|
|
210
|
+
}))
|
|
160
211
|
|
|
161
212
|
const createTxParams = {
|
|
162
213
|
assetClientInterface,
|
|
@@ -166,14 +217,15 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
166
217
|
amount,
|
|
167
218
|
nonce: resolvedNonce,
|
|
168
219
|
fromAddress,
|
|
169
|
-
eip1559Enabled,
|
|
170
220
|
customFee,
|
|
171
221
|
feeOpts,
|
|
172
222
|
txInput,
|
|
173
223
|
keepTxInput,
|
|
174
224
|
isSendAll,
|
|
175
225
|
createUnsignedTx,
|
|
226
|
+
feeData,
|
|
176
227
|
}
|
|
228
|
+
|
|
177
229
|
let { txId, rawTx, nonce, gasLimit, tipGasPrice, gasPrice } = await createTx(createTxParams)
|
|
178
230
|
|
|
179
231
|
const feeAmount = gasPrice.mul(gasLimit)
|
|
@@ -318,13 +370,13 @@ const createTx = async ({
|
|
|
318
370
|
amount,
|
|
319
371
|
nonce,
|
|
320
372
|
txInput,
|
|
321
|
-
eip1559Enabled = true,
|
|
322
373
|
keepTxInput = false,
|
|
323
|
-
customFee
|
|
374
|
+
customFee,
|
|
324
375
|
isSendAll,
|
|
325
376
|
fromAddress,
|
|
326
377
|
feeOpts,
|
|
327
378
|
createUnsignedTx,
|
|
379
|
+
feeData,
|
|
328
380
|
}) => {
|
|
329
381
|
assert(
|
|
330
382
|
nonce !== undefined && typeof nonce === 'number',
|
|
@@ -340,7 +392,7 @@ const createTx = async ({
|
|
|
340
392
|
? asset.contract.transfer.build(toAddress.toLowerCase(), amount.toBaseString())
|
|
341
393
|
: txInput
|
|
342
394
|
|
|
343
|
-
let { gasLimit, gasPrice, tipGasPrice } = await getFeeInfo({
|
|
395
|
+
let { gasLimit, gasPrice, tipGasPrice, eip1559Enabled } = await getFeeInfo({
|
|
344
396
|
assetClientInterface,
|
|
345
397
|
asset,
|
|
346
398
|
fromAddress,
|
|
@@ -348,6 +400,8 @@ const createTx = async ({
|
|
|
348
400
|
amount,
|
|
349
401
|
txInput,
|
|
350
402
|
feeOpts,
|
|
403
|
+
feeData,
|
|
404
|
+
customFee,
|
|
351
405
|
})
|
|
352
406
|
|
|
353
407
|
const isContractToAddress = await isContractAddressCached({ asset, address: toAddress })
|
|
@@ -361,22 +415,16 @@ const createTx = async ({
|
|
|
361
415
|
// versus estimations, anyway.
|
|
362
416
|
const isSendAllBaseAsset = isSendAll && !isToken && !isContractToAddress
|
|
363
417
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (isSendAllBaseAsset) {
|
|
377
|
-
// force consuming all gas
|
|
378
|
-
tipGasPrice = gasPrice
|
|
379
|
-
}
|
|
418
|
+
// For native send all transactions, we have to make sure that
|
|
419
|
+
// the `tipGasPrice` is equal to the `gasPrice`, since this is
|
|
420
|
+
// effectively like saying that the `maxFeePerGas` is equal
|
|
421
|
+
// to the `maxPriorityFeePerGas`. We do this so that for a
|
|
422
|
+
// fixed gas cost transaction, no dust balance should remain,
|
|
423
|
+
// since any deviation in the underlying `baseFeePerGas` will
|
|
424
|
+
// result only affect the tip for the miner - no dust remains.
|
|
425
|
+
if (eip1559Enabled && isSendAllBaseAsset) {
|
|
426
|
+
// force consuming all gas
|
|
427
|
+
tipGasPrice = gasPrice
|
|
380
428
|
}
|
|
381
429
|
|
|
382
430
|
// HACK: If we are handling a send all transaction, we must ensure
|
package/src/websocket/index.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This is a "light" version of @exodus/core resolution to select the native WebSocket on BE
|
|
3
|
-
* It's a workaround until https://github.com/ExodusMovement/assets/pull/72 is merged.
|
|
4
|
-
*
|
|
5
|
-
* Based on https://github.com/ExodusMovement/fetch/blob/master/core.js , note that chooses native on Desktop
|
|
6
|
-
*/
|
|
7
|
-
if (typeof process !== 'undefined' && (process.type === 'renderer' || process.type === 'worker')) {
|
|
8
|
-
// THIS IS FOR DESKTOP
|
|
9
|
-
if (process.env.EXODUS_DISABLE_WS) {
|
|
10
|
-
module.exports = globalThis.WebSocket
|
|
11
|
-
} else {
|
|
12
|
-
module.exports = require('ws')
|
|
13
|
-
}
|
|
14
|
-
// eslint-disable-next-line no-undef
|
|
15
|
-
} else if (typeof WebSocket === 'undefined') {
|
|
16
|
-
// THIS IS FOR UNIT TESTING.
|
|
17
|
-
module.exports = require('ws')
|
|
18
|
-
} else {
|
|
19
|
-
// THIS IS FOR BE
|
|
20
|
-
module.exports = globalThis.WebSocket
|
|
21
|
-
}
|