@exodus/ethereum-api 7.3.1 → 7.4.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/package.json +2 -2
- package/src/create-asset.js +19 -2
- package/src/get-balance-for-address.js +10 -0
- package/src/get-balances.js +35 -24
- package/src/get-fee.js +4 -2
- package/src/optimism-gas/index.js +42 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"cross-fetch": "^3.1.5",
|
|
58
58
|
"delay": "4.0.1"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "ac617aef893ac9d6b1689ca12e7b6de68a258621"
|
|
61
61
|
}
|
package/src/create-asset.js
CHANGED
|
@@ -36,6 +36,8 @@ import { createStakingApi } from './staking-api'
|
|
|
36
36
|
import { txSendFactory } from './tx-send'
|
|
37
37
|
import { signMessageWithSigner } from '@exodus/ethereum-lib/src/sign-message'
|
|
38
38
|
import { serverBasedFeeMonitorFactoryFactory } from './fee-monitor'
|
|
39
|
+
import { createGetBalanceForAddress } from './get-balance-for-address'
|
|
40
|
+
import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas'
|
|
39
41
|
|
|
40
42
|
export const createAssetFactory = ({
|
|
41
43
|
assetsList,
|
|
@@ -43,7 +45,6 @@ export const createAssetFactory = ({
|
|
|
43
45
|
AccountState: CustomAccountState,
|
|
44
46
|
monitorInterval,
|
|
45
47
|
nfts = false,
|
|
46
|
-
getFee: customGetFee,
|
|
47
48
|
useEip1191ChainIdChecksum = false,
|
|
48
49
|
customTokens = true,
|
|
49
50
|
isTestnet = false,
|
|
@@ -53,6 +54,7 @@ export const createAssetFactory = ({
|
|
|
53
54
|
customBip44,
|
|
54
55
|
customCreateGetKeyIdentifier,
|
|
55
56
|
feeMonitorInterval,
|
|
57
|
+
l1GasOracleAddress, // l1 extra fee for base and opto
|
|
56
58
|
}) => {
|
|
57
59
|
assert(assetsList, 'assetsList is required')
|
|
58
60
|
assert(feeData, 'feeData is required')
|
|
@@ -126,6 +128,7 @@ export const createAssetFactory = ({
|
|
|
126
128
|
nfts,
|
|
127
129
|
noHistory: monitorType === 'no-history',
|
|
128
130
|
signWithSigner: true,
|
|
131
|
+
signMessageWithSigner: true,
|
|
129
132
|
...(supportsStaking && { staking: {} }),
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -165,6 +168,17 @@ export const createAssetFactory = ({
|
|
|
165
168
|
const defaultAddressPath = 'm/0/0'
|
|
166
169
|
|
|
167
170
|
const sendTx = txSendFactory({ assetClientInterface })
|
|
171
|
+
|
|
172
|
+
const estimateL1DataFee = l1GasOracleAddress
|
|
173
|
+
? estimateL1DataFeeFactory({ l1GasOracleAddress, server })
|
|
174
|
+
: undefined
|
|
175
|
+
|
|
176
|
+
const originalGetFee = getFeeFactory({ gasLimit })
|
|
177
|
+
|
|
178
|
+
const getFee = l1GasOracleAddress
|
|
179
|
+
? getL1GetFeeFactory({ asset, originalGetFee })
|
|
180
|
+
: originalGetFee
|
|
181
|
+
|
|
168
182
|
const api = {
|
|
169
183
|
addressHasHistory,
|
|
170
184
|
broadcastTx: (...args) => server.sendRawTransaction(...args),
|
|
@@ -176,9 +190,10 @@ export const createAssetFactory = ({
|
|
|
176
190
|
defaultAddressPath,
|
|
177
191
|
features,
|
|
178
192
|
getBalances,
|
|
193
|
+
getBalanceForAddress: createGetBalanceForAddress({ asset, server }),
|
|
179
194
|
getConfirmationsNumber: () => confirmationNumber,
|
|
180
195
|
getDefaultAddressPath: () => defaultAddressPath,
|
|
181
|
-
getFee
|
|
196
|
+
getFee,
|
|
182
197
|
getFeeData: () => feeData,
|
|
183
198
|
getKeyIdentifier: createGetKeyIdentifier({ bip44, allowMetaMaskCompat }),
|
|
184
199
|
getSupportedPurposes: () => [44],
|
|
@@ -207,6 +222,8 @@ export const createAssetFactory = ({
|
|
|
207
222
|
keys,
|
|
208
223
|
address,
|
|
209
224
|
api,
|
|
225
|
+
estimateL1DataFee,
|
|
226
|
+
server,
|
|
210
227
|
...(erc20FuelBuffer && { erc20FuelBuffer }),
|
|
211
228
|
...(fuelThreshold && { fuelThreshold: asset.currency.defaultUnit(fuelThreshold) }),
|
|
212
229
|
getEffectiveGasPrice,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
export const createGetBalanceForAddress = ({ asset, server }) => {
|
|
4
|
+
assert(asset, 'asset is required')
|
|
5
|
+
assert(server, 'server is required')
|
|
6
|
+
return async (address) => {
|
|
7
|
+
const balances = await server.getBalance(address)
|
|
8
|
+
return asset.currency.baseUnit(balances.confirmed.value)
|
|
9
|
+
}
|
|
10
|
+
}
|
package/src/get-balances.js
CHANGED
|
@@ -2,21 +2,6 @@ import { isRpcBalanceAsset } from '@exodus/ethereum-lib'
|
|
|
2
2
|
|
|
3
3
|
import { get } from 'lodash'
|
|
4
4
|
|
|
5
|
-
const fixBalance = ({ txLog, balance }) => {
|
|
6
|
-
for (const tx of txLog) {
|
|
7
|
-
// TODO: pending can only be less than a few minutes old, we can only search the latest txs to improve performance
|
|
8
|
-
if (tx.sent && tx.pending && !tx.error) {
|
|
9
|
-
// coinAmount is negative for sent tx
|
|
10
|
-
balance = balance.sub(tx.coinAmount.abs())
|
|
11
|
-
if (tx.coinAmount.unitType.equals(tx.feeAmount.unitType)) {
|
|
12
|
-
balance = balance.sub(tx.feeAmount)
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return balance
|
|
18
|
-
}
|
|
19
|
-
|
|
20
5
|
const getBalanceFromAccountState = ({ asset, accountState }) => {
|
|
21
6
|
const isBase = asset.name === asset.baseAsset.name
|
|
22
7
|
return get(
|
|
@@ -44,14 +29,33 @@ const getUnstaked = ({ accountState, asset }) => {
|
|
|
44
29
|
)
|
|
45
30
|
}
|
|
46
31
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
const getUnconfirmedSentBalanceFromTxLog = ({ asset, txLog }) => {
|
|
33
|
+
let result = asset.currency.ZERO
|
|
34
|
+
|
|
35
|
+
for (const tx of txLog) {
|
|
36
|
+
const isUnconfirmed = !tx.failed && tx.pending
|
|
37
|
+
|
|
38
|
+
if (isUnconfirmed && tx.sent) {
|
|
39
|
+
result = result.add(tx.coinAmount.abs())
|
|
40
|
+
if (tx.feeAmount && tx.coinAmount.unitType.equals(tx.feeAmount.unitType)) {
|
|
41
|
+
result = result.add(tx.feeAmount.abs())
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
const getUnconfirmedReceivedBalanceFromTxLog = ({ asset, txLog }) => {
|
|
50
|
+
let result = asset.currency.ZERO
|
|
51
|
+
for (const tx of txLog) {
|
|
52
|
+
const isUnconfirmed = !tx.failed && tx.pending
|
|
53
|
+
if (isUnconfirmed && !tx.sent) {
|
|
54
|
+
result = result.add(tx.coinAmount.abs())
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return result
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
/**
|
|
@@ -63,13 +67,18 @@ function getBasicSpendable({ asset, accountState, txLog }) {
|
|
|
63
67
|
* @returns {{balance}|null} an object with the balance or null if the balance is unknown
|
|
64
68
|
*/
|
|
65
69
|
export const getBalances = ({ asset, txLog, accountState }) => {
|
|
66
|
-
const
|
|
70
|
+
const unconfirmedReceived = getUnconfirmedReceivedBalanceFromTxLog({ asset, txLog })
|
|
71
|
+
const unconfirmedSent = getUnconfirmedSentBalanceFromTxLog({ asset, txLog })
|
|
67
72
|
|
|
73
|
+
const balanceWithoutUnconfirmedSent = isRpcBalanceAsset(asset)
|
|
74
|
+
? getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
|
|
75
|
+
: getBalanceFromTxLog({ txLog, asset })
|
|
76
|
+
|
|
77
|
+
const spendable = balanceWithoutUnconfirmedSent.sub(unconfirmedReceived)
|
|
68
78
|
const staked = getStaked({ asset, accountState })
|
|
69
79
|
const unstaking = getUnstaking({ asset, accountState })
|
|
70
80
|
const unstaked = getUnstaked({ asset, accountState })
|
|
71
|
-
|
|
72
|
-
const total = spendable.add(staked).add(unstaking).add(unstaked)
|
|
81
|
+
const total = balanceWithoutUnconfirmedSent.add(staked).add(unstaking).add(unstaked)
|
|
73
82
|
|
|
74
83
|
return {
|
|
75
84
|
// new
|
|
@@ -78,6 +87,8 @@ export const getBalances = ({ asset, txLog, accountState }) => {
|
|
|
78
87
|
staked,
|
|
79
88
|
unstaking,
|
|
80
89
|
unstaked,
|
|
90
|
+
unconfirmedReceived,
|
|
91
|
+
unconfirmedSent,
|
|
81
92
|
// legacy
|
|
82
93
|
balance: total,
|
|
83
94
|
spendableBalance: spendable,
|
package/src/get-fee.js
CHANGED
|
@@ -5,7 +5,7 @@ const taxes = {
|
|
|
5
5
|
paxgold: 0.0002,
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const getGasPriceMultiplier = ({ asset, feeData, isExchange, isSendAll }) => {
|
|
8
|
+
const getGasPriceMultiplier = ({ asset, feeData, isExchange, isSendAll, isRbfAllowed }) => {
|
|
9
9
|
// exchanges quotes expire, do not risk having a stuck tx
|
|
10
10
|
if (isExchange) return 1
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@ const getGasPriceMultiplier = ({ asset, feeData, isExchange, isSendAll }) => {
|
|
|
14
14
|
if (isSendAll && isEthereumLike(asset) && feeData.eip1559Enabled) return 1
|
|
15
15
|
|
|
16
16
|
// do not risk having a stuck tx if we're not able to accelerate it
|
|
17
|
-
if (!feeData.rbfEnabled) return 1
|
|
17
|
+
if (!isRbfAllowed || !feeData.rbfEnabled) return 1
|
|
18
18
|
|
|
19
19
|
return feeData.gasPriceEconomicalRate
|
|
20
20
|
}
|
|
@@ -43,12 +43,14 @@ export const getFeeFactory =
|
|
|
43
43
|
isExchange,
|
|
44
44
|
isSendAll,
|
|
45
45
|
amount,
|
|
46
|
+
isRbfAllowed = true, // Destkop, isRbfAllowed=true when advanced panel is on
|
|
46
47
|
calculateEffectiveFee, // BE
|
|
47
48
|
customFee: customGasPrice, // BE
|
|
48
49
|
}) => {
|
|
49
50
|
const { gasPrice, eip1559Enabled, baseFeePerGas, tipGasPrice } = feeData
|
|
50
51
|
const gasPriceMultiplier = getGasPriceMultiplier({
|
|
51
52
|
asset,
|
|
53
|
+
isRbfAllowed,
|
|
52
54
|
feeData,
|
|
53
55
|
isExchange,
|
|
54
56
|
isSendAll,
|
|
@@ -3,19 +3,48 @@ import { createEthereumJsTx, createContract } from '@exodus/ethereum-lib'
|
|
|
3
3
|
import { getServerByName } from '../exodus-eth-server'
|
|
4
4
|
import { GAS_ORACLE_ADDRESS } from './addresses'
|
|
5
5
|
import { fromHexToBigInt } from '../number-utils'
|
|
6
|
+
import assert from 'minimalistic-assert'
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
export const estimateL1DataFeeFactory = ({ l1GasOracleAddress, server }) => {
|
|
9
|
+
assert(l1GasOracleAddress, 'l1GasOracleAddress is required')
|
|
10
|
+
assert(server, 'server is required')
|
|
11
|
+
const gasContract = createContract(l1GasOracleAddress, 'optimismGasOracle')
|
|
12
|
+
return async ({ unsignedTx }) => {
|
|
13
|
+
const ethjsTx = createEthereumJsTx(unsignedTx)
|
|
14
|
+
const serialized = ethjsTx.serialize()
|
|
15
|
+
const callData = gasContract.getL1Fee.build(serialized)
|
|
16
|
+
const buffer = Buffer.from(callData)
|
|
17
|
+
const data = ethUtil.bufferToHex(buffer)
|
|
18
|
+
const hex = await server.ethCall({ to: l1GasOracleAddress, data }, 'latest')
|
|
19
|
+
const l1DataFee = fromHexToBigInt(hex)
|
|
20
|
+
const padFee = l1DataFee / BigInt(4)
|
|
21
|
+
const maxL1DataFee = l1DataFee + padFee
|
|
22
|
+
return maxL1DataFee.toString()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getL1GetFeeFactory = ({ asset, originalGetFee }) => {
|
|
27
|
+
return ({ feeOpts, ...args }) => {
|
|
28
|
+
const { fee, ...rest } = originalGetFee(args)
|
|
29
|
+
if (!feeOpts?.optimismL1DataFee) {
|
|
30
|
+
// hardcoded optimism name is for back compatiblity, it could be base!
|
|
31
|
+
// better to rename
|
|
32
|
+
return { fee, ...rest }
|
|
33
|
+
}
|
|
8
34
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const callData = gasContract.getL1Fee.build(serialized)
|
|
13
|
-
const buffer = Buffer.from(callData)
|
|
14
|
-
const data = ethUtil.bufferToHex(buffer)
|
|
15
|
-
const server = getServerByName('optimism')
|
|
16
|
-
const hex = await server.ethCall({ to: GAS_ORACLE_ADDRESS, data }, 'latest')
|
|
17
|
-
const l1DataFee = fromHexToBigInt(hex)
|
|
18
|
-
const padFee = l1DataFee / BigInt(4)
|
|
19
|
-
const maxL1DataFee = l1DataFee + padFee
|
|
20
|
-
return maxL1DataFee.toString()
|
|
35
|
+
const l1DataFee = asset.currency.baseUnit(feeOpts.optimismL1DataFee)
|
|
36
|
+
return { fee: fee.add(l1DataFee), ...rest }
|
|
37
|
+
}
|
|
21
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Back-compatibility
|
|
42
|
+
*
|
|
43
|
+
* @deprecated use eth-asset.estimateL1DataFee
|
|
44
|
+
* @param unsignedTx
|
|
45
|
+
* @returns {Promise<string>}
|
|
46
|
+
*/
|
|
47
|
+
export const estimateOptimismL1DataFee = estimateL1DataFeeFactory({
|
|
48
|
+
server: getServerByName('optimism'),
|
|
49
|
+
l1GasOracleAddress: GAS_ORACLE_ADDRESS,
|
|
50
|
+
})
|