@exodus/ethereum-api 8.76.7 → 8.77.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,15 @@
|
|
|
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.77.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.76.7...@exodus/ethereum-api@8.77.0) (2026-06-29)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **ethereum:** implement getHistoricalBalance via clarity absolute balance ([#8281](https://github.com/ExodusMovement/assets/issues/8281)) ([be202a9](https://github.com/ExodusMovement/assets/commit/be202a94d9734d7af8ed118d11d4cc38c3b5ef10))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
6
15
|
## [8.76.7](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.76.6...@exodus/ethereum-api@8.76.7) (2026-06-26)
|
|
7
16
|
|
|
8
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.77.0",
|
|
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",
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"type": "git",
|
|
70
70
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "f156b9209a42ef331625b508a908659fd495c3e4"
|
|
73
73
|
}
|
package/src/create-asset.js
CHANGED
|
@@ -41,6 +41,7 @@ import { createFeeData } from './fee-data/index.js'
|
|
|
41
41
|
import { createGetBalanceForAddress } from './get-balance-for-address.js'
|
|
42
42
|
import { getBalancesFactory } from './get-balances.js'
|
|
43
43
|
import { getFeeFactory } from './get-fee.js'
|
|
44
|
+
import { getHistoricalBalanceFactory } from './get-historical-balance.js'
|
|
44
45
|
import { moveFundsFactory } from './move-funds.js'
|
|
45
46
|
import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas/index.js'
|
|
46
47
|
import { createSendValidations } from './send-validations.js'
|
|
@@ -177,12 +178,20 @@ export const createAssetFactory = ({
|
|
|
177
178
|
rpcBalanceAssetNames,
|
|
178
179
|
})
|
|
179
180
|
|
|
181
|
+
const historicalBalanceSupported = monitorType !== 'no-history'
|
|
182
|
+
const getHistoricalBalance = getHistoricalBalanceFactory({
|
|
183
|
+
assetClientInterface,
|
|
184
|
+
getBalances,
|
|
185
|
+
})
|
|
186
|
+
|
|
180
187
|
const { createToken, getTokens } = createTokenFactory(
|
|
181
188
|
{
|
|
182
189
|
address,
|
|
183
190
|
bip44,
|
|
184
191
|
keys,
|
|
185
192
|
getBalances,
|
|
193
|
+
getHistoricalBalance,
|
|
194
|
+
historicalBalance: historicalBalanceSupported,
|
|
186
195
|
assetClientInterface,
|
|
187
196
|
server,
|
|
188
197
|
stakingConfiguration,
|
|
@@ -225,6 +234,7 @@ export const createAssetFactory = ({
|
|
|
225
234
|
family: ASSET_FAMILY.EVM,
|
|
226
235
|
feeMonitor: true,
|
|
227
236
|
feesApi: true,
|
|
237
|
+
historicalBalance: historicalBalanceSupported,
|
|
228
238
|
isMaxFeeAsset,
|
|
229
239
|
isTestnet,
|
|
230
240
|
moveFunds: true,
|
|
@@ -336,6 +346,7 @@ export const createAssetFactory = ({
|
|
|
336
346
|
features,
|
|
337
347
|
getActivityTxs,
|
|
338
348
|
getBalances,
|
|
349
|
+
...(features.historicalBalance && { getHistoricalBalance }),
|
|
339
350
|
getBalanceForAddress: createGetBalanceForAddress({ asset, server }),
|
|
340
351
|
...(getBlackListStatus && { getBlackListStatus }),
|
|
341
352
|
getConfirmationsNumber: () => confirmationsNumber,
|
|
@@ -37,6 +37,8 @@ const getActivityTxs = ({ txs }) => txs.filter((tx) => !smallbalanceTx(tx))
|
|
|
37
37
|
const getCreateBaseToken =
|
|
38
38
|
({
|
|
39
39
|
getBalances,
|
|
40
|
+
getHistoricalBalance,
|
|
41
|
+
historicalBalance,
|
|
40
42
|
assetClientInterface,
|
|
41
43
|
server,
|
|
42
44
|
stakingConfiguration = Object.create(null),
|
|
@@ -53,6 +55,7 @@ const getCreateBaseToken =
|
|
|
53
55
|
features: tokenFeatures,
|
|
54
56
|
hasFeature: (feature) => !!tokenFeatures[feature], // @deprecated use api.features instead
|
|
55
57
|
getBalances,
|
|
58
|
+
...(historicalBalance && { getHistoricalBalance }),
|
|
56
59
|
getTxLogFilter: name === 'polygon' ? getPolygonTxLogFilter : getTxLogFilter,
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TxSet } from '@exodus/models'
|
|
2
|
+
import assert from 'minimalistic-assert'
|
|
3
|
+
|
|
4
|
+
import { getLatestCanonicalAbsoluteBalanceTx } from './tx-log/clarity-utils/index.js'
|
|
5
|
+
|
|
6
|
+
const walkBackFromBalance = ({ txLog, deadline, balance }) => {
|
|
7
|
+
let result = balance
|
|
8
|
+
|
|
9
|
+
for (const tx of txLog) {
|
|
10
|
+
if (new Date(tx.date).getTime() <= deadline) continue
|
|
11
|
+
if (tx.failed || tx.dropped || tx.pending || tx.data?.replacedBy) continue
|
|
12
|
+
|
|
13
|
+
result = result.sub(tx.coinAmount)
|
|
14
|
+
if (tx.feeAmount?.unitType.equals(tx.coinAmount.unitType)) {
|
|
15
|
+
result = result.add(tx.feeAmount)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return result.clampLowerZero()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds the Ethereum-like balance as of `date`. Anchors on the latest Clarity
|
|
24
|
+
* absolute-balance snapshot at or before the date, falling back to a walk-back
|
|
25
|
+
* from the current balance when no snapshot is available.
|
|
26
|
+
*/
|
|
27
|
+
export const getHistoricalBalanceFactory = ({ assetClientInterface, getBalances }) => {
|
|
28
|
+
assert(assetClientInterface, 'assetClientInterface is required')
|
|
29
|
+
assert(getBalances, 'getBalances is required')
|
|
30
|
+
|
|
31
|
+
return async ({ asset, walletAccount, date }) => {
|
|
32
|
+
assert(asset, 'asset is required')
|
|
33
|
+
assert(walletAccount, 'walletAccount is required')
|
|
34
|
+
assert(date instanceof Date, 'date is required')
|
|
35
|
+
|
|
36
|
+
const txLog = await assetClientInterface.getTxLog({ assetName: asset.name, walletAccount })
|
|
37
|
+
const deadline = date.getTime()
|
|
38
|
+
const anchor = getLatestCanonicalAbsoluteBalanceTx({ reversedTxLog: txLog.reverse(), deadline })
|
|
39
|
+
|
|
40
|
+
if (anchor) {
|
|
41
|
+
const total = asset.currency.baseUnit(anchor.data.balanceChange.to)
|
|
42
|
+
return { total, entries: [{ amount: total, date: deadline }] }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const accountState = await assetClientInterface.getAccountState({
|
|
46
|
+
assetName: asset.baseAsset.name,
|
|
47
|
+
walletAccount,
|
|
48
|
+
})
|
|
49
|
+
const confirmedTxLog = TxSet.fromArray([...txLog].filter((tx) => !tx.pending))
|
|
50
|
+
const balance = getBalances({ asset, accountState, txLog: confirmedTxLog }).spendable
|
|
51
|
+
const total = walkBackFromBalance({ txLog: confirmedTxLog, deadline, balance })
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
total,
|
|
55
|
+
entries: [{ amount: total, date: deadline }],
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/index.js
CHANGED
|
@@ -50,12 +50,15 @@ const getLatestCanonicalAbsoluteTx = ({
|
|
|
50
50
|
searchDepthMs = DEFAULT_CANONICAL_ABSOLUTE_TX_SEARCH_DEPTH,
|
|
51
51
|
reversedTxLog,
|
|
52
52
|
fieldName,
|
|
53
|
+
deadline = Infinity,
|
|
53
54
|
}) => {
|
|
54
55
|
assert(reversedTxLog, 'expected reversedTxLog')
|
|
55
56
|
|
|
56
57
|
let latest = null
|
|
57
58
|
|
|
58
59
|
for (const tx of reversedTxLog) {
|
|
60
|
+
if (+tx.date > deadline) continue
|
|
61
|
+
|
|
59
62
|
if (latest) {
|
|
60
63
|
const diff = +latest.date - +tx.date
|
|
61
64
|
|
|
@@ -77,10 +80,11 @@ const getLatestCanonicalAbsoluteTx = ({
|
|
|
77
80
|
return latest
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
export const getLatestCanonicalAbsoluteBalanceTx = ({ searchDepthMs, reversedTxLog }) =>
|
|
83
|
+
export const getLatestCanonicalAbsoluteBalanceTx = ({ searchDepthMs, reversedTxLog, deadline }) =>
|
|
81
84
|
getLatestCanonicalAbsoluteTx({
|
|
82
85
|
searchDepthMs,
|
|
83
86
|
reversedTxLog,
|
|
87
|
+
deadline,
|
|
84
88
|
fieldName: ABSOLUTE_FIELD_NAME_BALANCE_CHANGE,
|
|
85
89
|
})
|
|
86
90
|
|