@exodus/ethereum-api 8.41.0 → 8.42.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 +18 -0
- package/package.json +2 -2
- package/src/allowance/index.js +105 -32
- package/src/create-asset-utils.js +31 -0
- package/src/create-asset.js +4 -2
- package/src/index.js +1 -0
- package/src/tx-send/tx-send.js +1 -11
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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.42.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.42.0...@exodus/ethereum-api@8.42.1) (2025-07-11)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @exodus/ethereum-api
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [8.42.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.41.0...@exodus/ethereum-api@8.42.0) (2025-07-10)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
* feat: rig `getNonce` to evm asset api (#6047)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
## [8.41.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.40.1...@exodus/ethereum-api@8.41.0) (2025-07-01)
|
|
7
25
|
|
|
8
26
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.42.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",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"type": "git",
|
|
64
64
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "439848922f4e7a525c4e2373c99daefd89da402b"
|
|
67
67
|
}
|
package/src/allowance/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { isNumberUnit } from '@exodus/currency'
|
|
2
|
+
import { buffer2currency, createUnsignedTxFactory } from '@exodus/ethereum-lib'
|
|
3
|
+
import { bufferToInt } from '@exodus/ethereumjs/util'
|
|
4
|
+
import assert from 'minimalistic-assert'
|
|
5
|
+
|
|
1
6
|
import { getServer } from '../exodus-eth-server/index.js'
|
|
2
7
|
import { fetchGasLimit } from '../gas-estimation.js'
|
|
3
8
|
import { ALLOWANCE_TX_TIMEOUT, APPROVAL_GAS_LIMIT, ZERO_ALLOWANCE_ASSETS } from './constants.js'
|
|
@@ -27,8 +32,80 @@ export async function isSpendingApprovalRequired({
|
|
|
27
32
|
return tokenAmount.gt(allowance)
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
export const buildApproveTx = async ({
|
|
36
|
+
spenderAddress,
|
|
37
|
+
asset,
|
|
38
|
+
feeData,
|
|
39
|
+
fromAddress,
|
|
40
|
+
approveAmount = asset.currency.ZERO,
|
|
41
|
+
gasLimit,
|
|
42
|
+
txInput,
|
|
43
|
+
nonce,
|
|
44
|
+
walletAccount,
|
|
45
|
+
}) => {
|
|
46
|
+
assert(asset, 'expected asset')
|
|
47
|
+
assert(feeData, 'expected feeData')
|
|
48
|
+
assert(isNumberUnit(approveAmount), 'expected approveAmount')
|
|
49
|
+
assert(typeof spenderAddress === 'string', 'expected spenderAddress')
|
|
50
|
+
assert(walletAccount, 'expected walletAccount')
|
|
51
|
+
|
|
52
|
+
const { baseAsset } = asset
|
|
53
|
+
const chainId = baseAsset.chainId
|
|
54
|
+
assert(typeof chainId === 'number', 'expected chainId')
|
|
55
|
+
|
|
56
|
+
const createUnsignedTx = createUnsignedTxFactory({ chainId })
|
|
57
|
+
|
|
58
|
+
const toAddress = asset.contract.address
|
|
59
|
+
assert(typeof toAddress === 'string', 'expected contract address')
|
|
60
|
+
|
|
61
|
+
if (!txInput) txInput = asset.contract.approve.build(spenderAddress, approveAmount.toBaseString())
|
|
62
|
+
|
|
63
|
+
if (typeof nonce !== 'number')
|
|
64
|
+
nonce = await baseAsset.getNonce({ asset, fromAddress, walletAccount })
|
|
65
|
+
|
|
66
|
+
if (!gasLimit)
|
|
67
|
+
gasLimit = await fetchGasLimit({
|
|
68
|
+
asset: baseAsset,
|
|
69
|
+
amount: baseAsset.currency.ZERO,
|
|
70
|
+
toAddress,
|
|
71
|
+
txInput,
|
|
72
|
+
feeData,
|
|
73
|
+
isContract: true,
|
|
74
|
+
fromAddress,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return createUnsignedTx({
|
|
78
|
+
asset,
|
|
79
|
+
address: toAddress,
|
|
80
|
+
amount: baseAsset.currency.ZERO,
|
|
81
|
+
nonce,
|
|
82
|
+
txInput,
|
|
83
|
+
gasLimit,
|
|
84
|
+
gasPrice: feeData.gasPrice,
|
|
85
|
+
tipGasPrice: feeData.tipGasPrice,
|
|
86
|
+
fromAddress,
|
|
87
|
+
eip1559Enabled: feeData.eip1559Enabled,
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const getLegacyApproveTxFromUnsignedTx = ({ baseAsset, unsignedTx }) => ({
|
|
92
|
+
asset: baseAsset.name,
|
|
93
|
+
receiver: {
|
|
94
|
+
address: unsignedTx.txData.to,
|
|
95
|
+
amount: buffer2currency({ asset: baseAsset, value: unsignedTx.txData.value }),
|
|
96
|
+
},
|
|
97
|
+
txInput: unsignedTx.txData.data,
|
|
98
|
+
gasPrice: buffer2currency({ asset: baseAsset, value: unsignedTx.txData.gasPrice }),
|
|
99
|
+
tipGasPrice: unsignedTx.txData.tipGasPrice
|
|
100
|
+
? buffer2currency({ asset: baseAsset, value: unsignedTx.txData.tipGasPrice })
|
|
101
|
+
: undefined,
|
|
102
|
+
gasLimit: bufferToInt(unsignedTx.txData.gasLimit),
|
|
103
|
+
fromAddress: unsignedTx.txMeta.fromAddress,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// @deprecated use buildApproveTx instead
|
|
30
107
|
export const createApprove =
|
|
31
|
-
({ sendTx }) =>
|
|
108
|
+
({ assetClientInterface, sendTx }) =>
|
|
32
109
|
async ({
|
|
33
110
|
spenderAddress,
|
|
34
111
|
asset,
|
|
@@ -36,45 +113,41 @@ export const createApprove =
|
|
|
36
113
|
fromAddress,
|
|
37
114
|
approveAmount,
|
|
38
115
|
gasLimit,
|
|
39
|
-
txInput
|
|
40
|
-
|
|
116
|
+
txInput,
|
|
117
|
+
nonce,
|
|
118
|
+
walletAccount,
|
|
119
|
+
...extras
|
|
41
120
|
}) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const toAddress = asset.contract.address
|
|
47
|
-
const gasLimitOptions = {
|
|
48
|
-
asset: baseAsset,
|
|
49
|
-
amount: baseAsset.currency.ZERO,
|
|
50
|
-
toAddress,
|
|
51
|
-
txInput,
|
|
121
|
+
const unsignedTx = await buildApproveTx({
|
|
122
|
+
spenderAddress,
|
|
123
|
+
asset,
|
|
52
124
|
feeData,
|
|
53
|
-
isContract: true,
|
|
54
125
|
fromAddress,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
gasLimit = gasLimit || (await fetchGasLimit(gasLimitOptions))
|
|
58
|
-
|
|
59
|
-
const txData = await sendTx({
|
|
60
|
-
asset: baseAsset.name,
|
|
61
|
-
receiver: {
|
|
62
|
-
address: toAddress,
|
|
63
|
-
amount: baseAsset.currency.ZERO,
|
|
64
|
-
},
|
|
65
|
-
txInput,
|
|
66
|
-
gasPrice: feeData.gasPrice,
|
|
126
|
+
approveAmount,
|
|
67
127
|
gasLimit,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
128
|
+
txInput,
|
|
129
|
+
nonce,
|
|
130
|
+
walletAccount,
|
|
71
131
|
})
|
|
72
132
|
|
|
73
|
-
if (!
|
|
74
|
-
|
|
133
|
+
if (!assetClientInterface) {
|
|
134
|
+
const approveTx = getLegacyApproveTxFromUnsignedTx({ baseAsset: asset.baseAsset, unsignedTx })
|
|
135
|
+
const txData = await sendTx({ walletAccount, ...approveTx, silent: true, ...extras })
|
|
136
|
+
|
|
137
|
+
if (!txData || !txData.txId)
|
|
138
|
+
throw new Error(`Failed to approve ${asset.displayTicker} - ${spenderAddress}`)
|
|
139
|
+
|
|
140
|
+
return txData
|
|
75
141
|
}
|
|
76
142
|
|
|
77
|
-
|
|
143
|
+
const { txId, rawTx } = await assetClientInterface.signTransaction({
|
|
144
|
+
assetName: asset.baseAsset.name,
|
|
145
|
+
unsignedTx,
|
|
146
|
+
walletAccount,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
await asset.baseAsset.api.broadcastTx(rawTx.toString('hex'))
|
|
150
|
+
return { txId }
|
|
78
151
|
}
|
|
79
152
|
|
|
80
153
|
const createSendApprovalAndWatchConfirmation =
|
|
@@ -6,6 +6,7 @@ import { createEthereumHooks } from './hooks/index.js'
|
|
|
6
6
|
import { ClarityMonitor } from './tx-log/clarity-monitor.js'
|
|
7
7
|
import { EthereumMonitor } from './tx-log/ethereum-monitor.js'
|
|
8
8
|
import { EthereumNoHistoryMonitor } from './tx-log/ethereum-no-history-monitor.js'
|
|
9
|
+
import { resolveNonce } from './tx-send/nonce-utils.js'
|
|
9
10
|
|
|
10
11
|
// Determines the appropriate `monitorType`, `serverUrl` and `monitorInterval`
|
|
11
12
|
// to use for a given config.
|
|
@@ -151,3 +152,33 @@ export const createHistoryMonitorFactory = ({
|
|
|
151
152
|
return monitor
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
|
|
156
|
+
export const getNonceFactory = ({ assetClientInterface, useAbsoluteBalanceAndNonce }) => {
|
|
157
|
+
assert(assetClientInterface, 'expected assetClientInterface')
|
|
158
|
+
assert(typeof useAbsoluteBalanceAndNonce === 'boolean', 'expected useAbsoluteBalanceAndNonce')
|
|
159
|
+
|
|
160
|
+
const getNonce = async ({ asset, fromAddress, walletAccount }) => {
|
|
161
|
+
assert(asset, 'expected asset')
|
|
162
|
+
assert(typeof fromAddress === 'string', 'expected fromAddress')
|
|
163
|
+
assert(walletAccount, 'expected walletAccount')
|
|
164
|
+
|
|
165
|
+
const txLog = await assetClientInterface.getTxLog({
|
|
166
|
+
assetName: asset.baseAsset.name,
|
|
167
|
+
walletAccount,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return resolveNonce({
|
|
171
|
+
asset,
|
|
172
|
+
fromAddress,
|
|
173
|
+
txLog,
|
|
174
|
+
// For assets where we'll fall back to querying the coin node, we
|
|
175
|
+
// search for pending transactions. For base assets with history,
|
|
176
|
+
// we'll fall back to the `TxLog` since this also has a knowledge
|
|
177
|
+
// of which transactions are currently in pending.
|
|
178
|
+
tag: 'pending',
|
|
179
|
+
useAbsoluteNonce: useAbsoluteBalanceAndNonce,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { getNonce }
|
|
184
|
+
}
|
package/src/create-asset.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { connectAssetsList } from '@exodus/assets'
|
|
2
|
-
import { pick } from '@exodus/basic-utils'
|
|
3
2
|
import bip44Constants from '@exodus/bip44-constants/by-ticker.js'
|
|
4
3
|
import {
|
|
5
4
|
createEthereumLikeAccountState,
|
|
@@ -24,6 +23,7 @@ import { addressHasHistoryFactory } from './address-has-history.js'
|
|
|
24
23
|
import {
|
|
25
24
|
createHistoryMonitorFactory,
|
|
26
25
|
createTransactionPrivacyFactory,
|
|
26
|
+
getNonceFactory,
|
|
27
27
|
resolveMonitorSettings,
|
|
28
28
|
} from './create-asset-utils.js'
|
|
29
29
|
import { createTokenFactory } from './create-token-factory.js'
|
|
@@ -237,6 +237,8 @@ export const createAssetFactory = ({
|
|
|
237
237
|
? getL1GetFeeFactory({ asset, originalGetFee })
|
|
238
238
|
: originalGetFee
|
|
239
239
|
|
|
240
|
+
const { getNonce } = getNonceFactory({ assetClientInterface, useAbsoluteBalanceAndNonce })
|
|
241
|
+
|
|
240
242
|
const api = {
|
|
241
243
|
addressHasHistory,
|
|
242
244
|
broadcastTx: (...args) => server.sendRawTransaction(...args),
|
|
@@ -280,7 +282,6 @@ export const createAssetFactory = ({
|
|
|
280
282
|
|
|
281
283
|
const fullAsset = {
|
|
282
284
|
...asset,
|
|
283
|
-
...pick(config, ['accountReserve']),
|
|
284
285
|
gasLimit,
|
|
285
286
|
contractGasLimit,
|
|
286
287
|
bip44,
|
|
@@ -293,6 +294,7 @@ export const createAssetFactory = ({
|
|
|
293
294
|
broadcastPrivateBundle,
|
|
294
295
|
broadcastPrivateTx,
|
|
295
296
|
forceGasLimitEstimation,
|
|
297
|
+
getNonce,
|
|
296
298
|
privacyServer,
|
|
297
299
|
server,
|
|
298
300
|
...(erc20FuelBuffer && { erc20FuelBuffer }),
|
package/src/index.js
CHANGED
package/src/tx-send/tx-send.js
CHANGED
|
@@ -204,17 +204,7 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx, useAbsoluteBala
|
|
|
204
204
|
const resolvedNonce =
|
|
205
205
|
providedNonce ??
|
|
206
206
|
bumpNonce ??
|
|
207
|
-
(await
|
|
208
|
-
asset,
|
|
209
|
-
fromAddress,
|
|
210
|
-
txLog: baseAssetTxLog,
|
|
211
|
-
// For assets where we'll fall back to querying the coin node, we
|
|
212
|
-
// search for pending transactions. For base assets with history,
|
|
213
|
-
// we'll fall back to the `TxLog` since this also has a knowledge
|
|
214
|
-
// of which transactions are currently in pending.
|
|
215
|
-
tag: 'pending',
|
|
216
|
-
useAbsoluteNonce: useAbsoluteBalanceAndNonce,
|
|
217
|
-
}))
|
|
207
|
+
(await asset.baseAsset.getNonce({ asset, fromAddress, walletAccount }))
|
|
218
208
|
|
|
219
209
|
const createTxParams = {
|
|
220
210
|
assetClientInterface,
|