@exodus/ethereum-api 8.78.1 → 8.78.2
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 +23 -0
- package/package.json +2 -2
- package/src/create-asset-utils.js +29 -0
- package/src/exodus-eth-server/ws-gateway.js +2 -2
- package/src/fee-utils.js +9 -0
- package/src/index.js +10 -9
- package/src/simulate-tx/simulate-eth-tx.js +19 -6
- package/src/tx-log/clarity-monitor.js +3 -1
- package/src/tx-log/clarity-truncated-history-monitor.js +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
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.78.2](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.77.1...@exodus/ethereum-api@8.78.2) (2026-08-14)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* source eip7702 delegation whitelist from remote config ([#8356](https://github.com/ExodusMovement/assets/issues/8356)) ([1a0966b](https://github.com/ExodusMovement/assets/commit/1a0966b08befb6e9310993f323c21d003dea2754))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **ethereum-api:** detect legacy delegate transactions ([#8357](https://github.com/ExodusMovement/assets/issues/8357)) ([070e529](https://github.com/ExodusMovement/assets/commit/070e5295f21f12695e97e721ff8c0601c553e447))
|
|
17
|
+
* **ethereum-api:** preserve precision for wei values in tx simulation ([#7920](https://github.com/ExodusMovement/assets/issues/7920)) ([5c99258](https://github.com/ExodusMovement/assets/commit/5c9925871d450167fcf23e33ba62e100a2d71a9b))
|
|
18
|
+
* **ethereum-api:** reference defined ethereum asset in simulate-tx tests ([#8525](https://github.com/ExodusMovement/assets/issues/8525)) ([27a8918](https://github.com/ExodusMovement/assets/commit/27a8918f5ba3112f1830ac0111a43d30921aa3b3))
|
|
19
|
+
* **ethereum:** prevent silently dropped zero-tip private bundles ([#8493](https://github.com/ExodusMovement/assets/issues/8493)) ([67dd1ff](https://github.com/ExodusMovement/assets/commit/67dd1ffd7e1c22e7119b1781968c4be321ab1822))
|
|
20
|
+
* **lint:** fix import/named warnings and upgrade to error ([#8496](https://github.com/ExodusMovement/assets/issues/8496)) ([8d290c9](https://github.com/ExodusMovement/assets/commit/8d290c948a0d3c2c1897287cb133f9d26cad73b0))
|
|
21
|
+
* **lint:** fix JSDoc warnings across multiple packages ([#8519](https://github.com/ExodusMovement/assets/issues/8519)) ([5fefcd5](https://github.com/ExodusMovement/assets/commit/5fefcd5af996efbe9587cecba540efc7f469ceb2))
|
|
22
|
+
* **lint:** fix no-throw-literal warnings and upgrade to error ([#8498](https://github.com/ExodusMovement/assets/issues/8498)) ([8fc68eb](https://github.com/ExodusMovement/assets/commit/8fc68eb65682e0b5a7da9415410ee1dedf309696))
|
|
23
|
+
* **lint:** fix oxlint warnings and convert rules to error ([#8489](https://github.com/ExodusMovement/assets/issues/8489)) ([19d35be](https://github.com/ExodusMovement/assets/commit/19d35be718391303d331978926884f2f376b356f))
|
|
24
|
+
* **simulation:** route recipient changes to dedicated field, guard asset-less entries ([#8370](https://github.com/ExodusMovement/assets/issues/8370)) ([c0a082b](https://github.com/ExodusMovement/assets/commit/c0a082b6c80710408337ff9285a19fb657c656be))
|
|
25
|
+
* upgrade low-complexity lint rules to error ([#8510](https://github.com/ExodusMovement/assets/issues/8510)) ([25016bd](https://github.com/ExodusMovement/assets/commit/25016bdf43526c6bc9efe2771f92f1ae1ebf7cd7))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
## [8.78.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.77.1...@exodus/ethereum-api@8.78.1) (2026-08-13)
|
|
7
30
|
|
|
8
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.78.
|
|
3
|
+
"version": "8.78.2",
|
|
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",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"type": "git",
|
|
68
68
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "07b29ff2dc71fb1358ad6b1b6748c3f9fe66c10e"
|
|
71
71
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BlacklistCheckTypes } from '@exodus/asset-lib'
|
|
2
|
+
import { parseUnsignedTx } from '@exodus/ethereum-lib'
|
|
2
3
|
import { defaultAbiCoder } from '@exodus/ethereumjs/ethers5-abi'
|
|
3
4
|
import { safeString } from '@exodus/safe-string'
|
|
4
5
|
import assert from 'minimalistic-assert'
|
|
@@ -112,6 +113,29 @@ const assertSendPrivateTxProps = ({ asset, unsignedTx, walletAccount }) => {
|
|
|
112
113
|
assert(walletAccount, 'expected walletAccount')
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
// Block builders drop private transactions that carry no bid — accepted by
|
|
117
|
+
// the relay but never included on chain (ExodusMovement/assets#8084). The
|
|
118
|
+
// effective bid is min(tipGasPrice, gasPrice - baseFeePerGas): `gasPrice` is
|
|
119
|
+
// the maxFeePerGas ceiling for EIP-1559 transactions and the all-in price
|
|
120
|
+
// for legacy ones, so either term at zero kills the bid.
|
|
121
|
+
const assertNonZeroTipGasPrice = async ({ asset, assetClientInterface, unsignedTx }) => {
|
|
122
|
+
const { eip1559Enabled, gasPrice, tipGasPrice } = parseUnsignedTx({ asset, unsignedTx })
|
|
123
|
+
|
|
124
|
+
if (eip1559Enabled && (!tipGasPrice || tipGasPrice.isZero)) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'ERR_PRIVATE_TX_ZERO_TIP: transaction pays no tipGasPrice; block builders drop zero-bid private transactions'
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const feeData = await assetClientInterface.getFeeConfig({ assetName: asset.baseAsset.name })
|
|
131
|
+
const baseFeePerGas = feeData?.baseFeePerGas
|
|
132
|
+
if (!baseFeePerGas || gasPrice.gt(baseFeePerGas)) return
|
|
133
|
+
|
|
134
|
+
throw new Error(
|
|
135
|
+
`ERR_PRIVATE_TX_ZERO_TIP: transaction gasPrice (${gasPrice.toBaseString()}) does not exceed baseFeePerGas (${baseFeePerGas.toBaseString()}); block builders drop zero-bid private transactions`
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
115
139
|
export const createTransactionPrivacyResult = ({
|
|
116
140
|
assetClientInterface,
|
|
117
141
|
broadcastPrivateBundle,
|
|
@@ -123,6 +147,7 @@ export const createTransactionPrivacyResult = ({
|
|
|
123
147
|
|
|
124
148
|
const sendPrivateTx = async ({ asset, unsignedTx, walletAccount }) => {
|
|
125
149
|
assertSendPrivateTxProps({ asset, unsignedTx, walletAccount })
|
|
150
|
+
await assertNonZeroTipGasPrice({ asset, assetClientInterface, unsignedTx })
|
|
126
151
|
|
|
127
152
|
const { rawTx, txId } = await signTx({
|
|
128
153
|
asset,
|
|
@@ -160,6 +185,10 @@ export const createTransactionPrivacyResult = ({
|
|
|
160
185
|
|
|
161
186
|
unsignedTxBundle.forEach(assertSendPrivateTxProps)
|
|
162
187
|
|
|
188
|
+
for (const { asset, unsignedTx } of unsignedTxBundle) {
|
|
189
|
+
await assertNonZeroTipGasPrice({ asset, assetClientInterface, unsignedTx })
|
|
190
|
+
}
|
|
191
|
+
|
|
163
192
|
const signTxResults = await Promise.all(
|
|
164
193
|
unsignedTxBundle.map(({ asset, unsignedTx, walletAccount }) =>
|
|
165
194
|
signTx({ assetClientInterface, asset, unsignedTx, walletAccount })
|
|
@@ -21,8 +21,8 @@ const parseSubscriptionKey = (subscriptionKey) => {
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @typedef subscribeWalletAddresses
|
|
24
|
-
* @property {string} network
|
|
25
|
-
* @property {string} address
|
|
24
|
+
* @property {string} network - The network identifier
|
|
25
|
+
* @property {string} address - The wallet address to subscribe
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
export default class WsGateway extends EventEmitter {
|
package/src/fee-utils.js
CHANGED
|
@@ -111,9 +111,18 @@ export const calculateEthLikeFeeMonitorUpdateEip1559 = async ({
|
|
|
111
111
|
// at least the `baseFeePerGas`.
|
|
112
112
|
const gasPrice = baseFeePerGas > defaultGasPrice ? baseFeePerGas : defaultGasPrice
|
|
113
113
|
|
|
114
|
+
// `eth_gasPrice` embeds the node's prevailing tip on top of the
|
|
115
|
+
// `baseFeePerGas`. Without an explicit `tipGasPrice`, `refineFeeConfig`
|
|
116
|
+
// collapses the effective gas price to the bare base fee, producing
|
|
117
|
+
// zero-tip transactions that private bundle relays drop silently.
|
|
118
|
+
const inferredTipGasPrice = gasPrice - baseFeePerGas
|
|
119
|
+
|
|
114
120
|
const defaultFeeConfig = {
|
|
115
121
|
gasPrice: `${gasPrice.toString()} wei`,
|
|
116
122
|
baseFeePerGas: `${baseFeePerGas.toString()} wei`,
|
|
123
|
+
...(inferredTipGasPrice > BigInt(0) && {
|
|
124
|
+
tipGasPrice: `${inferredTipGasPrice.toString()} wei`,
|
|
125
|
+
}),
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
// Determine if there are any `rewardPercentiles` specified in the
|
package/src/index.js
CHANGED
|
@@ -41,28 +41,29 @@ export { getStakingHistoryBalance, getBalancesFactory } from './get-balances.js'
|
|
|
41
41
|
export { getHistoricalBalanceFactory } from './get-historical-balance.js'
|
|
42
42
|
export { createEncodeMultisigContract } from './multisig-address.js'
|
|
43
43
|
|
|
44
|
+
export { FantomStaking, stakingProviderClientFactory } from './staking/index.js'
|
|
45
|
+
export { ethereumStakingDeps } from './staking/ethereum/deps.js'
|
|
46
|
+
export { getEthereumStakingInfo, createEthereumStakingService } from './staking/ethereum/service.js'
|
|
47
|
+
export { EthereumStaking } from './staking/ethereum/api.js'
|
|
44
48
|
export {
|
|
45
|
-
FantomStaking,
|
|
46
|
-
stakingProviderClientFactory,
|
|
47
|
-
ethereumStakingDeps,
|
|
48
|
-
getEthereumStakingInfo,
|
|
49
|
-
createEthereumStakingService,
|
|
50
|
-
EthereumStaking,
|
|
51
49
|
isEthereumStakingTx,
|
|
52
50
|
isEthereumDelegate,
|
|
53
51
|
isEthereumUndelegatePending,
|
|
54
52
|
isEthereumUndelegate,
|
|
55
53
|
isEthereumClaimUndelegate,
|
|
56
|
-
|
|
54
|
+
} from './staking/ethereum/staking-utils.js'
|
|
55
|
+
export { MaticStakingApi } from './staking/matic/index.js'
|
|
56
|
+
export {
|
|
57
57
|
isPolygonTx,
|
|
58
58
|
isPolygonDelegate,
|
|
59
59
|
isPolygonUndelegate,
|
|
60
60
|
isPolygonReward,
|
|
61
61
|
isPolygonClaimUndelegate,
|
|
62
62
|
isPendingPolygonUndelegateTxInEthereumTxLog,
|
|
63
|
-
} from './staking/
|
|
63
|
+
} from './staking/matic/matic-staking-utils.js'
|
|
64
64
|
|
|
65
|
-
export { fetchTxPreview
|
|
65
|
+
export { fetchTxPreview } from './simulate-tx/fetch-tx-preview.js'
|
|
66
|
+
export { maybeRemoveDuplicates, retrieveSideEffects } from './simulate-tx/simulate-eth-tx.js'
|
|
66
67
|
export { simulateTransactions } from './simulation/simulate-transactions.js'
|
|
67
68
|
|
|
68
69
|
export {
|
|
@@ -8,11 +8,22 @@ import SolidityContract from '@exodus/solidity-contract'
|
|
|
8
8
|
import assert from 'minimalistic-assert'
|
|
9
9
|
|
|
10
10
|
import { getERC20Params } from '../eth-like-util.js'
|
|
11
|
+
import { fromHexToBigInt } from '../number-utils.js'
|
|
11
12
|
import { fetchTxPreview } from './fetch-tx-preview.js'
|
|
12
13
|
|
|
13
14
|
const ethDecimals = ethereum.units.ETH
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
// Convert a hex transaction field (e.g. "0x10a741a462780000" for 1.2 ETH in wei)
|
|
17
|
+
// to a precision-preserving decimal string. `parseInt` would silently truncate
|
|
18
|
+
// values above Number.MAX_SAFE_INTEGER (~9e15), and Ethereum wei amounts routinely
|
|
19
|
+
// exceed that (1 ETH = 1e18 wei).
|
|
20
|
+
const ethHexToDecimalString = (hexValue) => {
|
|
21
|
+
if (hexValue === undefined || hexValue === null || hexValue === '' || hexValue === '0x') {
|
|
22
|
+
return '0'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return fromHexToBigInt(hexValue).toString()
|
|
26
|
+
}
|
|
16
27
|
|
|
17
28
|
async function getAssetSymbolFromContract(contractAddress, asset) {
|
|
18
29
|
const { symbol: assetSymbol } = await getERC20Params({
|
|
@@ -169,14 +180,16 @@ export async function retrieveSideEffects({ transaction, asset, shouldSimulate =
|
|
|
169
180
|
|
|
170
181
|
if (!transaction.to) throw new Error(`'to' field is missing in the TX object`)
|
|
171
182
|
|
|
183
|
+
const txValueDecimal = ethHexToDecimalString(transaction.value)
|
|
184
|
+
|
|
172
185
|
const blocknativeTxObject = {
|
|
173
186
|
to: transaction.to,
|
|
174
187
|
input: transaction.data,
|
|
175
188
|
from: transaction.from,
|
|
176
|
-
value:
|
|
177
|
-
gas:
|
|
178
|
-
maxFeePerGas:
|
|
179
|
-
maxPriorityFeePerGas:
|
|
189
|
+
value: txValueDecimal,
|
|
190
|
+
gas: ethHexToDecimalString(transaction.gas),
|
|
191
|
+
maxFeePerGas: ethHexToDecimalString(transaction.maxFeePerGas),
|
|
192
|
+
maxPriorityFeePerGas: ethHexToDecimalString(transaction.maxPriorityFeePerGas),
|
|
180
193
|
}
|
|
181
194
|
|
|
182
195
|
try {
|
|
@@ -224,7 +237,7 @@ export async function retrieveSideEffects({ transaction, asset, shouldSimulate =
|
|
|
224
237
|
if (willSend.length === 0) {
|
|
225
238
|
willSend.push({
|
|
226
239
|
symbol: 'ETH',
|
|
227
|
-
balance: `-${
|
|
240
|
+
balance: `-${txValueDecimal}`,
|
|
228
241
|
assetType: 'ether',
|
|
229
242
|
decimal: ethDecimals,
|
|
230
243
|
})
|
|
@@ -284,7 +284,9 @@ export class ClarityMonitor extends BaseMonitor {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
if (historyError) {
|
|
287
|
-
throw historyError
|
|
287
|
+
throw historyError instanceof Error
|
|
288
|
+
? historyError
|
|
289
|
+
: new Error(String(historyError), { cause: historyError })
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
if (refresh || hasNewTxs) {
|
|
@@ -229,7 +229,9 @@ export class ClarityTruncatedHistoryMonitor extends ClarityMonitor {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
if (historyError) {
|
|
232
|
-
throw historyError
|
|
232
|
+
throw historyError instanceof Error
|
|
233
|
+
? historyError
|
|
234
|
+
: new Error(String(historyError), { cause: historyError })
|
|
233
235
|
}
|
|
234
236
|
|
|
235
237
|
if (refresh || hasNewTxs) {
|