@exodus/ethereum-lib 5.10.1 → 5.11.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.
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
|
+
## [5.11.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-lib@5.10.2...@exodus/ethereum-lib@5.11.0) (2025-05-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* feat: import eth-sig-util sign-typed-data, with fixes (#5716)
|
|
13
|
+
|
|
14
|
+
* feat: unify ethereum signMessage with signMessageWithSigner (#5709)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [5.10.2](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-lib@5.10.1...@exodus/ethereum-lib@5.10.2) (2025-04-29)
|
|
19
|
+
|
|
20
|
+
**Note:** Version bump only for package @exodus/ethereum-lib
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [5.10.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-lib@5.10.0...@exodus/ethereum-lib@5.10.1) (2025-04-15)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-lib",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Ethereum utils, such as for cryptography, address encoding/decoding, transaction building, etc.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@exodus/basic-utils": "^3.0.1",
|
|
25
|
+
"@exodus/crypto": "^1.0.0-rc.18",
|
|
25
26
|
"@exodus/currency": "^6.0.1",
|
|
26
|
-
"@exodus/ethereumjs": "^1.
|
|
27
|
+
"@exodus/ethereumjs": "^1.5.0",
|
|
27
28
|
"@exodus/key-utils": "^3.7.0",
|
|
28
29
|
"@exodus/models": "^12.0.1",
|
|
29
30
|
"@exodus/solidity-contract": "^1.1.3",
|
|
30
|
-
"@metamask/eth-sig-util": "^4.0.1",
|
|
31
31
|
"base-x": "^3.0.2",
|
|
32
32
|
"lodash": "^4.17.15",
|
|
33
33
|
"minimalistic-assert": "^1.0.1",
|
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
"@exodus/assets": "^11.0.0",
|
|
39
39
|
"@exodus/bitcoin-meta": "^2.0.0",
|
|
40
40
|
"@exodus/bsc-meta": "^2.0.0",
|
|
41
|
-
"@exodus/crypto": "^1.0.0-rc.13",
|
|
42
41
|
"@exodus/ethereum-meta": "^2.5.0",
|
|
43
42
|
"@exodus/ethereumclassic-meta": "^2.0.0",
|
|
44
43
|
"@exodus/fantommainnet-meta": "^2.0.0",
|
|
@@ -51,5 +50,5 @@
|
|
|
51
50
|
"type": "git",
|
|
52
51
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
53
52
|
},
|
|
54
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "45be970be70f9211b8a6489566c352f4caeb82dc"
|
|
55
54
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
1
2
|
import ms from 'ms'
|
|
2
3
|
import { createSelector } from 'reselect'
|
|
3
4
|
|
|
@@ -85,20 +86,136 @@ const wrapResponseToObject = ({ bumpType = BumpType.NONE, errorMessage = null }
|
|
|
85
86
|
|
|
86
87
|
const calculateTxGasPrice = (tx) => tx.feeAmount.div(tx.data.gasLimit)
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
const calculateBumpedGasPriceNonEip1559 = ({ currentGasPrice, prevMaxFeePerGas }) => {
|
|
90
|
+
assert(currentGasPrice, 'currentGasPrice is required')
|
|
91
|
+
assert(prevMaxFeePerGas, 'prevMaxFeePerGas is required')
|
|
92
|
+
|
|
93
|
+
// Pick the largest between the network's `currentGasPrice`
|
|
94
|
+
// and the maximum `gasPrice` the previous transaction was
|
|
95
|
+
// willing to send at.
|
|
96
|
+
const nextGasPrice = currentGasPrice.gt(prevMaxFeePerGas) ? currentGasPrice : prevMaxFeePerGas
|
|
97
|
+
|
|
98
|
+
// Suggest using an amplified `gasPrice`.
|
|
99
|
+
return {
|
|
100
|
+
bumpedGasPrice: nextGasPrice.mul(BUMP_RATE),
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const getLinearIncentiveBumpForEip1559Transaction = ({
|
|
105
|
+
currentBaseFeePerGas,
|
|
106
|
+
prevMaxFeePerGas,
|
|
107
|
+
prevMaxPriorityFeePerGas,
|
|
108
|
+
}) => {
|
|
109
|
+
assert(currentBaseFeePerGas, 'currentBaseFeePerGas is required')
|
|
110
|
+
assert(prevMaxFeePerGas, 'prevMaxFeePerGas is required')
|
|
111
|
+
assert(prevMaxPriorityFeePerGas, 'prevMaxPriorityFeePerGas is required')
|
|
112
|
+
|
|
113
|
+
// First, let's determine if there's a bump required just to bring
|
|
114
|
+
// the transaction up to the `baseFeePerGas`. If the current base
|
|
115
|
+
// fee is larger, we'll need to see how much to adjust the overall
|
|
116
|
+
// transaction by.
|
|
117
|
+
const increaseToBaseFeePerGasTipGasPrice = currentBaseFeePerGas.gt(prevMaxFeePerGas)
|
|
118
|
+
? currentBaseFeePerGas.sub(prevMaxFeePerGas)
|
|
119
|
+
: currentBaseFeePerGas.sub(currentBaseFeePerGas) // TODO: What is a nice way to get ZERO without an `asset`? Is it `currentBaseFeePerGas.ZERO`?
|
|
120
|
+
|
|
121
|
+
// The new `bumpedTipGasPrice`.
|
|
122
|
+
const bumpedTipGasPrice = prevMaxPriorityFeePerGas
|
|
123
|
+
.mul(BUMP_RATE)
|
|
124
|
+
.add(increaseToBaseFeePerGasTipGasPrice)
|
|
125
|
+
|
|
126
|
+
// We'll make attempt a linear increase of the transaction pricing. By
|
|
127
|
+
// increasing the `tipGasPrice` and the `gasPrice` by the same amount,
|
|
128
|
+
// we ensuret he any additional amount we spend (over the basefee) goes
|
|
129
|
+
// directly to the miner, thereby increasing transaction incentive.
|
|
130
|
+
return {
|
|
131
|
+
bumpedGasPrice: prevMaxFeePerGas
|
|
132
|
+
.sub(prevMaxPriorityFeePerGas) // Removes the previous `tipGasPrice` before adding the next one (`bumpedTipGasPrice` is inclusive).
|
|
133
|
+
.add(bumpedTipGasPrice),
|
|
134
|
+
bumpedTipGasPrice,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const calculateBumpedGasPriceEip1559 = ({
|
|
139
|
+
currentBaseFeePerGas,
|
|
140
|
+
prevMaxFeePerGas,
|
|
141
|
+
prevMaxPriorityFeePerGas,
|
|
142
|
+
}) => {
|
|
143
|
+
assert(currentBaseFeePerGas, 'currentBaseFeePerGas is required')
|
|
144
|
+
assert(prevMaxFeePerGas, 'prevMaxFeePerGas is required')
|
|
145
|
+
|
|
146
|
+
// If the previous transaction didn't define a `tipGasPrice`, we
|
|
147
|
+
// can use this value directly.
|
|
148
|
+
if (!prevMaxPriorityFeePerGas || prevMaxPriorityFeePerGas.isZero) {
|
|
149
|
+
// We can determine the intended transaction bump using
|
|
150
|
+
// pre EIP-1559 logic, where we compare the `currentBaseFeePerGas`
|
|
151
|
+
// (i.e. the network gas price without a miner incentive) versus
|
|
152
|
+
// the `currentMaxFeePerGas` (which we can the previous `gasPrice`
|
|
153
|
+
// for the transaction). In this regard, the logic of finding the
|
|
154
|
+
// `bumpedGasPrice` to increase to is identical.
|
|
155
|
+
const { bumpedGasPrice } = calculateBumpedGasPriceNonEip1559({
|
|
156
|
+
currentGasPrice: currentBaseFeePerGas,
|
|
157
|
+
prevMaxFeePerGas,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
assert(bumpedGasPrice >= currentBaseFeePerGas, 'bumpedGasPrice must be >= currentBaseFeePerGas')
|
|
161
|
+
|
|
162
|
+
// Since `calculateBumpedGasPriceNonEip1559` will guarantee that
|
|
163
|
+
// the returned `bumpedGasPrice` will be greater than the
|
|
164
|
+
// `currentBaseFeePerGas` (and the `currentMaxFeePerGas`), we can
|
|
165
|
+
// use their difference to deterime the equivalent `bumpTipGasPrice`.
|
|
166
|
+
const bumpedTipGasPrice = bumpedGasPrice.sub(currentBaseFeePerGas)
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
bumpedGasPrice: currentBaseFeePerGas.add(bumpedTipGasPrice),
|
|
170
|
+
bumpedTipGasPrice,
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Else, let's make a linear increase to the transaction tip. This
|
|
175
|
+
// increase should ensure that:
|
|
176
|
+
// 1. The `prevMaxPriorityFeePerGas` is multiplied by the `BUMP_RATE`.
|
|
177
|
+
// 2. The returned `bumpedGasPrice` is sufficient for the
|
|
178
|
+
// `currentBaseFeePerGas`.
|
|
179
|
+
return getLinearIncentiveBumpForEip1559Transaction({
|
|
180
|
+
currentBaseFeePerGas,
|
|
181
|
+
prevMaxFeePerGas,
|
|
182
|
+
prevMaxPriorityFeePerGas,
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const calculateBumpedGasPrice = ({
|
|
187
|
+
baseAsset,
|
|
188
|
+
tx,
|
|
189
|
+
currentBaseFee,
|
|
190
|
+
currentGasPrice,
|
|
191
|
+
eip1559Enabled,
|
|
192
|
+
}) => {
|
|
193
|
+
// Determine the transaction we intend to override's
|
|
194
|
+
// `maxFeePerGas` (for pre-EIP-1559 networks, this is the
|
|
195
|
+
// `gasPrice` of the previous transaction).
|
|
196
|
+
const prevMaxFeePerGas = calculateTxGasPrice(tx)
|
|
197
|
+
|
|
198
|
+
if (!eip1559Enabled)
|
|
199
|
+
return calculateBumpedGasPriceNonEip1559({
|
|
200
|
+
currentGasPrice,
|
|
201
|
+
prevMaxFeePerGas,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
if (!currentBaseFee) {
|
|
205
|
+
// NOTE: This can result in differences between predicted and realized fees.
|
|
206
|
+
currentBaseFee = currentGasPrice
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const prevMaxPriorityFeePerGas = tx.data?.tipGasPrice
|
|
210
|
+
? baseAsset.currency.baseUnit(tx.data.tipGasPrice)
|
|
211
|
+
: null
|
|
212
|
+
|
|
213
|
+
return calculateBumpedGasPriceEip1559({
|
|
214
|
+
baseAsset,
|
|
215
|
+
currentBaseFeePerGas: currentBaseFee,
|
|
216
|
+
prevMaxPriorityFeePerGas,
|
|
217
|
+
prevMaxFeePerGas,
|
|
218
|
+
})
|
|
102
219
|
}
|
|
103
220
|
|
|
104
221
|
export const canAccelerateTx = ({
|
|
@@ -130,19 +247,23 @@ export const canAccelerateTx = ({
|
|
|
130
247
|
if (isQueuedPendingTx(tx, baseAssetName, activeWalletAccount, getTxLog))
|
|
131
248
|
return wrapResponseToObject({ errorMessage: 'there is a stuck TX with lower nonce' })
|
|
132
249
|
|
|
133
|
-
const {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
250
|
+
const {
|
|
251
|
+
gasPrice: currentGasPrice,
|
|
252
|
+
eip1559Enabled,
|
|
253
|
+
baseFeePerGas: currentBaseFee,
|
|
254
|
+
} = getFeeData(assetName)
|
|
137
255
|
|
|
138
256
|
const { bumpedGasPrice: gasPriceToUse } = calculateBumpedGasPrice({
|
|
139
257
|
baseAsset,
|
|
140
258
|
tx,
|
|
141
259
|
currentGasPrice,
|
|
260
|
+
currentBaseFee,
|
|
142
261
|
eip1559Enabled,
|
|
143
262
|
})
|
|
144
263
|
const replacementFee = gasPriceToUse.mul(tx.data.gasLimit)
|
|
264
|
+
|
|
145
265
|
const extraEthNeeded = replacementFee.sub(tx.feeAmount)
|
|
266
|
+
|
|
146
267
|
if (!getIsEnoughBalanceToAccelerate(activeWalletAccount, baseAssetName, extraEthNeeded))
|
|
147
268
|
return wrapResponseToObject({ errorMessage: 'insufficient funds' })
|
|
148
269
|
|
package/src/sign-message.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ecdsaSignHash } from '@exodus/crypto/secp256k1'
|
|
2
2
|
import {
|
|
3
|
-
personalSign,
|
|
4
|
-
signTypedData,
|
|
5
3
|
SignTypedDataVersion,
|
|
6
4
|
TypedDataUtils,
|
|
7
5
|
typedSignatureHash,
|
|
8
|
-
} from '@
|
|
6
|
+
} from '@exodus/ethereumjs/eth-sig-util'
|
|
7
|
+
import { hashPersonalMessage } from '@exodus/ethereumjs/util'
|
|
9
8
|
import assert from 'minimalistic-assert'
|
|
10
9
|
|
|
11
10
|
import { normalizeRecoveryParam } from './utils/ecdsa.js'
|
|
@@ -19,41 +18,19 @@ function hex0xStringToBuffer(hex) {
|
|
|
19
18
|
return Buffer.from(hexWithLeadingZeros, 'hex')
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
throw new TypeError('expected hex string')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return '0x' + hex
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const signMessage = async ({ privateKey, message }) => {
|
|
42
|
-
const { rawMessage, EIP712Message } = message
|
|
43
|
-
|
|
44
|
-
// Exclusive OR (XOR)
|
|
45
|
-
assert(!!rawMessage !== !!EIP712Message, 'Need either rawMessage or EIP712Message')
|
|
46
|
-
|
|
47
|
-
if (rawMessage) {
|
|
48
|
-
return hex0xStringToBuffer(personalSign({ privateKey, data: bufferToHex0xString(rawMessage) }))
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (EIP712Message) {
|
|
52
|
-
const version = Array.isArray(EIP712Message) ? SignTypedDataVersion.V1 : SignTypedDataVersion.V4
|
|
53
|
-
|
|
54
|
-
return hex0xStringToBuffer(signTypedData({ privateKey, data: EIP712Message, version }))
|
|
55
|
-
}
|
|
56
|
-
}
|
|
21
|
+
export const signMessage = async ({ privateKey, message }) =>
|
|
22
|
+
signMessageWithSigner({
|
|
23
|
+
message,
|
|
24
|
+
signer: {
|
|
25
|
+
sign: async ({ data }) =>
|
|
26
|
+
ecdsaSignHash({
|
|
27
|
+
hash: data,
|
|
28
|
+
privateKey,
|
|
29
|
+
recovery: true,
|
|
30
|
+
extraEntropy: null, // TODO: can we flip this to true?
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
})
|
|
57
34
|
|
|
58
35
|
/**
|
|
59
36
|
* @param {object} params
|