@exodus/ethereum-api 9.0.0-alpha.3 → 9.0.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 +2139 -0
- package/LICENSE +7 -0
- package/README.md +124 -0
- package/package.json +34 -33
- package/src/address-has-history.js +6 -2
- package/src/allowance/constants.js +4 -5
- package/src/allowance/index.js +159 -40
- package/src/check-tx/create-assess-transaction.js +23 -0
- package/src/check-tx/create-check-tx.js +104 -0
- package/src/check-tx/index.js +1 -0
- package/src/create-asset-plugin-factory.js +115 -0
- package/src/create-asset-utils.js +520 -0
- package/src/create-asset.js +273 -97
- package/src/create-token-factory.js +47 -17
- package/src/custom-fees.js +57 -0
- package/src/eip7702-whitelist.js +62 -0
- package/src/ens/index.js +12 -17
- package/src/error-wrapper.js +383 -0
- package/src/eth-like-util.js +172 -33
- package/src/exodus-eth-server/api-coin-nodes.js +27 -196
- package/src/exodus-eth-server/api.js +23 -14
- package/src/exodus-eth-server/clarity-v2.js +255 -0
- package/src/exodus-eth-server/clarity.js +67 -265
- package/src/exodus-eth-server/errors.js +40 -0
- package/src/exodus-eth-server/eth-like-server.js +385 -0
- package/src/exodus-eth-server/fetch-json.js +48 -0
- package/src/exodus-eth-server/index.js +77 -0
- package/src/exodus-eth-server/utils.js +31 -0
- package/src/exodus-eth-server/ws-gateway.js +306 -0
- package/src/exodus-eth-server/ws.js +1 -2
- package/src/fee-data/EthLikeFeeData.js +81 -0
- package/src/fee-data/index.js +54 -0
- package/src/fee-utils.js +229 -0
- package/src/gas-estimation.js +183 -66
- package/src/get-balance-for-address.js +1 -1
- package/src/get-balances.js +285 -27
- package/src/get-fee.js +92 -56
- package/src/get-historical-balance.js +58 -0
- package/src/hooks/index.js +1 -1
- package/src/hooks/monitor.js +182 -43
- package/src/hooks/staking-freshness.js +58 -0
- package/src/index.js +118 -12
- package/src/move-funds.js +153 -0
- package/src/multicall3/index.js +212 -0
- package/src/multisig-address.js +146 -0
- package/src/nft-utils.js +60 -0
- package/src/number-utils.js +34 -2
- package/src/optimism-gas/index.js +13 -5
- package/src/send-validations.js +58 -0
- package/src/server-based-fee-monitor.js +12 -19
- package/src/simulate-tx/fetch-tx-preview.js +0 -2
- package/src/simulate-tx/index.js +2 -2
- package/src/simulate-tx/simulate-eth-tx.js +24 -11
- package/src/simulation/common.js +34 -0
- package/src/simulation/create-simulate-message.js +49 -0
- package/src/simulation/create-simulate-transactions.js +108 -0
- package/src/simulation/estimate-fee.js +14 -0
- package/src/simulation/estimate-simple-transfer.js +15 -0
- package/src/simulation/get-message-type.js +18 -0
- package/src/simulation/simulate-message-api.js +68 -0
- package/src/simulation/simulate-transactions-api.js +266 -0
- package/src/simulation/simulate-transactions.js +16 -0
- package/src/simulation/transactions.js +52 -0
- package/src/simulation/try-estimating-changes-locally.js +26 -0
- package/src/staking/api/index.js +43 -0
- package/src/staking/cached-read.js +33 -0
- package/src/staking/ethereum/api.js +166 -34
- package/src/staking/ethereum/deps.js +37 -0
- package/src/staking/ethereum/everstake.js +113 -0
- package/src/staking/ethereum/index.js +4 -3
- package/src/staking/ethereum/service.js +682 -181
- package/src/staking/ethereum/staking-utils.js +275 -4
- package/src/staking/fantom-staking.js +9 -9
- package/src/staking/index.js +4 -4
- package/src/staking/matic/addresses.js +2 -0
- package/src/staking/matic/api.js +69 -49
- package/src/staking/matic/index.js +3 -3
- package/src/staking/matic/matic-staking-utils.js +243 -5
- package/src/staking/matic/matic-staking.md +53 -0
- package/src/staking/staking-provider-client.js +1 -1
- package/src/staking/utils/index.js +39 -0
- package/src/tx-create.js +666 -0
- package/src/tx-log/clarity-monitor.js +276 -103
- package/src/tx-log/clarity-truncated-history-monitor.js +130 -0
- package/src/tx-log/clarity-truncated-history-monitor.md +219 -0
- package/src/tx-log/clarity-utils/absolute.js +96 -0
- package/src/tx-log/clarity-utils/get-log-items-from-server-tx.js +94 -31
- package/src/tx-log/clarity-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
- package/src/tx-log/clarity-utils/index.js +9 -3
- package/src/tx-log/clarity-utils/is-spam-pending-tx.js +13 -0
- package/src/tx-log/clarity-utils/normalize-transactions-response.js +65 -0
- package/src/tx-log/ethereum-monitor.js +93 -47
- package/src/tx-log/ethereum-no-history-monitor.js +52 -41
- package/src/tx-log/get-optimistic-txlog-effects.js +379 -0
- package/src/tx-log/index.js +5 -3
- package/src/tx-log/monitor-utils/check-pending-transactions.js +14 -5
- package/src/tx-log/monitor-utils/exclude-unchanged-token-balances.js +1 -1
- package/src/tx-log/monitor-utils/extract-balance-from-clarity-account-info.js +108 -0
- package/src/tx-log/monitor-utils/get-balance-updates.js +44 -0
- package/src/tx-log/monitor-utils/get-batched-rpc-balances.js +47 -0
- package/src/tx-log/monitor-utils/get-current-blacklist-status.js +38 -0
- package/src/tx-log/monitor-utils/get-current-eip7702-delegation.js +79 -0
- package/src/tx-log/monitor-utils/get-derive-transactions-to-check.js +10 -5
- package/src/tx-log/monitor-utils/get-fee-amount.js +1 -1
- package/src/tx-log/monitor-utils/get-history-from-server.js +1 -1
- package/src/tx-log/monitor-utils/get-log-items-from-server-tx.js +75 -33
- package/src/tx-log/monitor-utils/get-names-of-tokens-transferred-by-server-tx.js +3 -2
- package/src/tx-log/monitor-utils/get-raw-transfer-amount.js +29 -0
- package/src/tx-log/monitor-utils/get-transfers-by-token-name.js +3 -2
- package/src/tx-log/monitor-utils/get-value-of-transfers.js +10 -12
- package/src/tx-log/monitor-utils/index.js +12 -7
- package/src/tx-log/monitor-utils/is-unverifiable-pending-tx-expired.js +19 -0
- package/src/tx-log/monitor-utils/verify-pending-tx-status-rpc.js +90 -0
- package/src/tx-log-staking-processor/asset-staking-tx-data.js +82 -24
- package/src/tx-log-staking-processor/index.js +28 -14
- package/src/tx-log-staking-processor/utils.js +21 -20
- package/src/tx-send/broadcast-error-handler.js +63 -0
- package/src/tx-send/index.js +1 -2
- package/src/tx-send/nonce-utils.js +97 -0
- package/src/tx-send/tx-send.js +94 -288
- package/src/tx-sign/index.js +17 -0
- package/src/tx-type/index.js +280 -0
- package/src/watch-tx.js +36 -0
- package/src/web3/createSimulateMessage.js +3 -2
- package/src/web3/createSimulateTransactions.js +11 -0
- package/src/web3/index.js +3 -1
- package/src/etherscan/account.js +0 -49
- package/src/etherscan/index.js +0 -28
- package/src/etherscan/logs.js +0 -16
- package/src/etherscan/proxy.js +0 -47
- package/src/etherscan/request.js +0 -26
- package/src/etherscan/ws.js +0 -88
- package/src/exodus-eth-server/server-factory.js +0 -28
- package/src/exodus-eth-server/server-map.js +0 -21
- package/src/get-fee-async.js +0 -97
- package/src/staking/matic/service.js +0 -437
- package/src/staking-api.js +0 -5
- package/src/tx-log/clarity-utils/get-derive-data-needed-for-tick.js +0 -12
- package/src/tx-log-staking-processor/get-asset-tx-amount.js +0 -26
- package/src/tx-send/get-fee-info.js +0 -41
- package/src/websocket/index.android.js +0 -2
- package/src/websocket/index.ios.js +0 -2
- package/src/websocket/index.js +0 -21
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { pick } from '@exodus/basic-utils'
|
|
2
|
+
import { createCurrency, makeSimulationAPICall } from '@exodus/web3-utils'
|
|
3
|
+
|
|
4
|
+
import { BLOWFISH_EVM_CHAINS, MAX_INT256_SOLIDITY } from './common.js'
|
|
5
|
+
|
|
6
|
+
const convertTransactionToApiPayload = (transactions, origin, blockNumber, recipientAddress) => {
|
|
7
|
+
const payload = {
|
|
8
|
+
txObjects: transactions.map((transaction) => ({
|
|
9
|
+
from: transaction.from,
|
|
10
|
+
to: transaction.to,
|
|
11
|
+
data: transaction.data,
|
|
12
|
+
value: transaction.value,
|
|
13
|
+
gas: transaction.gas,
|
|
14
|
+
gas_price: transaction.gasPrice,
|
|
15
|
+
})),
|
|
16
|
+
metadata: recipientAddress ? { origin, recipientAddress } : { origin },
|
|
17
|
+
userAccount: transactions[0].from,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (blockNumber !== undefined) {
|
|
21
|
+
payload.blockNumber = blockNumber
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return payload
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const createBalanceAmount = (amount, { decimals, symbol }) => {
|
|
28
|
+
const beforeAmount = createCurrency({
|
|
29
|
+
amount: amount.before,
|
|
30
|
+
symbol,
|
|
31
|
+
denominator: decimals,
|
|
32
|
+
})
|
|
33
|
+
const afterAmount = createCurrency({
|
|
34
|
+
amount: amount.after,
|
|
35
|
+
symbol,
|
|
36
|
+
denominator: decimals,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return beforeAmount.sub(afterAmount)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleExpectedBalanceChange = (expectedBalanceChange, balanceChanges, { network }) => {
|
|
43
|
+
const { willApprove, willSend, willReceive } = balanceChanges
|
|
44
|
+
const { data, kind } = expectedBalanceChange.rawInfo
|
|
45
|
+
|
|
46
|
+
if (kind === 'FARCASTER_CHANGE_RECOVERY_ADDRESS' || kind === 'ERC20_PERMIT') {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { asset: simulationAsset } = data
|
|
51
|
+
|
|
52
|
+
if (!simulationAsset) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const isNftTransfer =
|
|
57
|
+
kind === 'ERC721_TRANSFER' || (kind === 'ERC1155_TRANSFER' && !simulationAsset.decimals)
|
|
58
|
+
const isNftTransferApproval =
|
|
59
|
+
kind === 'ERC721_APPROVAL' ||
|
|
60
|
+
(kind === 'ERC721_APPROVAL_FOR_ALL' && !simulationAsset.decimals) ||
|
|
61
|
+
(kind === 'ERC1155_APPROVAL_FOR_ALL' && !simulationAsset.decimals)
|
|
62
|
+
const decimals =
|
|
63
|
+
isNftTransfer || isNftTransferApproval || !Number.isFinite(simulationAsset.decimals)
|
|
64
|
+
? 0
|
|
65
|
+
: simulationAsset.decimals
|
|
66
|
+
const simulationAssetInfo = pick(simulationAsset, [
|
|
67
|
+
'address',
|
|
68
|
+
'imageUrl',
|
|
69
|
+
'name',
|
|
70
|
+
'symbol',
|
|
71
|
+
'verified',
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
let symbol = simulationAsset.symbol || simulationAsset.name || 'NFT'
|
|
75
|
+
if (network === 'polygon' && simulationAsset.symbol === 'MATIC') {
|
|
76
|
+
symbol = 'POL'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isNftTransfer || isNftTransferApproval) {
|
|
80
|
+
simulationAssetInfo.imageUrl = data?.metadata?.rawImageUrl
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
kind === 'ERC20_APPROVAL' ||
|
|
85
|
+
kind === 'ERC721_APPROVAL_FOR_ALL' ||
|
|
86
|
+
kind === 'ERC1155_APPROVAL_FOR_ALL' ||
|
|
87
|
+
kind === 'ERC721_APPROVAL'
|
|
88
|
+
) {
|
|
89
|
+
const unitsToUse = symbol || simulationAsset.name || 'Units.'
|
|
90
|
+
|
|
91
|
+
const approvingBalance = createCurrency({
|
|
92
|
+
amount: data.amount.after,
|
|
93
|
+
symbol: unitsToUse,
|
|
94
|
+
denominator: decimals,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
willApprove.push({
|
|
98
|
+
asset: simulationAssetInfo,
|
|
99
|
+
spender: data.spender.address,
|
|
100
|
+
unitName: unitsToUse,
|
|
101
|
+
balance: approvingBalance,
|
|
102
|
+
isMaxApproval: approvingBalance._number.gte(MAX_INT256_SOLIDITY),
|
|
103
|
+
})
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { amount } = data
|
|
108
|
+
|
|
109
|
+
const balance = createBalanceAmount(amount, { decimals, symbol })
|
|
110
|
+
|
|
111
|
+
if (balance.isZero) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const balanceChange = {
|
|
116
|
+
asset: simulationAssetInfo,
|
|
117
|
+
balance,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (isNftTransfer || isNftTransferApproval) {
|
|
121
|
+
const tokenId = data.tokenId
|
|
122
|
+
const compositeId = `${simulationAsset.address}/${tokenId}`
|
|
123
|
+
balanceChange.nft = {
|
|
124
|
+
id: `${network}:${compositeId}`,
|
|
125
|
+
compositeId,
|
|
126
|
+
title: simulationAsset.name,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (balance.isNegative) {
|
|
131
|
+
balanceChange.balance = balanceChange.balance.negate()
|
|
132
|
+
|
|
133
|
+
willReceive.push(balanceChange)
|
|
134
|
+
} else {
|
|
135
|
+
willSend.push(balanceChange)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const SimulationErrorMessages = {
|
|
140
|
+
APICallFailed: 0,
|
|
141
|
+
SimulationFailed: 1,
|
|
142
|
+
SimulationNotSupported: 2,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const simulateTransactionsApi = async ({
|
|
146
|
+
transactions,
|
|
147
|
+
apiEndpoint,
|
|
148
|
+
origin,
|
|
149
|
+
blockNumber,
|
|
150
|
+
recipientAddress,
|
|
151
|
+
simulationResult,
|
|
152
|
+
headers,
|
|
153
|
+
asset,
|
|
154
|
+
}) => {
|
|
155
|
+
const successDefaultResult = {
|
|
156
|
+
kind: 'success',
|
|
157
|
+
value: undefined,
|
|
158
|
+
}
|
|
159
|
+
if (!BLOWFISH_EVM_CHAINS[asset.name]) {
|
|
160
|
+
return {
|
|
161
|
+
kind: 'error',
|
|
162
|
+
error: SimulationErrorMessages.SimulationNotSupported,
|
|
163
|
+
errorMessage: 'Simulation is not supported for this asset.',
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const userAddress = transactions[0].from
|
|
168
|
+
|
|
169
|
+
const payload = convertTransactionToApiPayload(
|
|
170
|
+
transactions,
|
|
171
|
+
origin,
|
|
172
|
+
blockNumber,
|
|
173
|
+
recipientAddress
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if (!Object.hasOwnProperty.call(BLOWFISH_EVM_CHAINS, asset.name)) {
|
|
177
|
+
return successDefaultResult
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const { network, chain } = BLOWFISH_EVM_CHAINS[asset.name]
|
|
181
|
+
|
|
182
|
+
const response = await makeSimulationAPICall({
|
|
183
|
+
url: apiEndpoint,
|
|
184
|
+
chain,
|
|
185
|
+
network,
|
|
186
|
+
payload,
|
|
187
|
+
headers,
|
|
188
|
+
})
|
|
189
|
+
if (!response) {
|
|
190
|
+
return {
|
|
191
|
+
kind: 'error',
|
|
192
|
+
error: SimulationErrorMessages.APICallFailed,
|
|
193
|
+
errorMessage: 'Simulation API call failed.',
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// eslint-disable-next-line @exodus/hydra/no-param-reassign-prop-only
|
|
198
|
+
simulationResult.metadata.simulatedLocally = false
|
|
199
|
+
|
|
200
|
+
const { expectedStateChanges = {}, error } = response.simulationResults.aggregated
|
|
201
|
+
|
|
202
|
+
if (error) {
|
|
203
|
+
return {
|
|
204
|
+
kind: 'error',
|
|
205
|
+
error: SimulationErrorMessages.SimulationFailed,
|
|
206
|
+
errorMessage: error?.humanReadableError,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const { action } = response
|
|
211
|
+
|
|
212
|
+
if (action === 'BLOCK') {
|
|
213
|
+
simulationResult.warnings.push({
|
|
214
|
+
kind: 'MALICIOUS_ACTION',
|
|
215
|
+
severity: 'CRITICAL',
|
|
216
|
+
message: 'This dApp could be malicious. Do not proceed unless you are certain this is safe.',
|
|
217
|
+
})
|
|
218
|
+
} else if (action === 'WARN') {
|
|
219
|
+
simulationResult.warnings.push({
|
|
220
|
+
kind: 'SUSPICIOUS_ACTION',
|
|
221
|
+
severity: 'MEDIUM',
|
|
222
|
+
message: 'This transaction may be suspicious. Proceed with caution.',
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const userAddressLower = userAddress.toLowerCase()
|
|
227
|
+
const recipientAddressLower =
|
|
228
|
+
recipientAddress && recipientAddress.toLowerCase() !== userAddressLower
|
|
229
|
+
? recipientAddress.toLowerCase()
|
|
230
|
+
: undefined
|
|
231
|
+
|
|
232
|
+
const relevantExpectedChanges = Object.entries(expectedStateChanges).flatMap(
|
|
233
|
+
([address, changes]) => {
|
|
234
|
+
const addressLower = address.toLowerCase()
|
|
235
|
+
const isUserOwned = addressLower === userAddressLower
|
|
236
|
+
if (!isUserOwned && addressLower !== recipientAddressLower) {
|
|
237
|
+
return []
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return (changes ?? []).map((change) => ({ change, isUserOwned }))
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
const noExpectedStateChangesDetected = relevantExpectedChanges.length === 0
|
|
245
|
+
|
|
246
|
+
if (noExpectedStateChangesDetected) {
|
|
247
|
+
simulationResult.balanceChanges.willSend.push({
|
|
248
|
+
balance: createCurrency({
|
|
249
|
+
amount: 0,
|
|
250
|
+
symbol: asset.displayTicker,
|
|
251
|
+
denominator: asset.currency.defaultUnit.power,
|
|
252
|
+
}),
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
return successDefaultResult
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
relevantExpectedChanges.forEach(({ change, isUserOwned }) => {
|
|
259
|
+
const balanceChanges = isUserOwned
|
|
260
|
+
? simulationResult.balanceChanges
|
|
261
|
+
: simulationResult.recipientBalanceChanges
|
|
262
|
+
handleExpectedBalanceChange(change, balanceChanges, { network })
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
return successDefaultResult
|
|
266
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
import { createSimulateTransactions } from './create-simulate-transactions.js'
|
|
4
|
+
|
|
5
|
+
const simulateEVMTransactions = createSimulateTransactions({
|
|
6
|
+
apiEndpoint: 'https://simulation.a.exodus.io/simulate',
|
|
7
|
+
headers: {
|
|
8
|
+
'X-Api-Version': '2023-06-05',
|
|
9
|
+
},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
export function simulateTransactions({ asset, transactions, ...restParameters }) {
|
|
13
|
+
assert(asset, '"asset" should be passed.')
|
|
14
|
+
|
|
15
|
+
return simulateEVMTransactions({ asset, transactions, ...restParameters })
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import SolidityContract from '@exodus/solidity-contract'
|
|
2
|
+
|
|
3
|
+
export function getDisplayDetails(willApprove) {
|
|
4
|
+
let title = 'Approve Transaction?'
|
|
5
|
+
const isApproveTransaction = willApprove.length
|
|
6
|
+
|
|
7
|
+
if (isApproveTransaction) {
|
|
8
|
+
const tokenName = willApprove[0].unitName
|
|
9
|
+
title = tokenName ? `Approve ${tokenName} Transactions?` : title
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return { title }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const isSimpleTransfer = (transaction) => {
|
|
16
|
+
return ['0x', null, undefined].includes(transaction.data)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const getTxFeeDetails = (transaction) => {
|
|
20
|
+
const maxFeePerGas = transaction.maxFeePerGas
|
|
21
|
+
const gasPrice = transaction.gasPrice
|
|
22
|
+
const gasLimit = transaction.gas
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
gasLimit,
|
|
26
|
+
maxFeePerGas,
|
|
27
|
+
gasPrice,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function decodeRecipientAddresses(transaction) {
|
|
32
|
+
try {
|
|
33
|
+
if (!transaction.to) {
|
|
34
|
+
return []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isSimpleTransfer(transaction)) {
|
|
38
|
+
return [transaction.to]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const token = SolidityContract.erc20(transaction.to)
|
|
42
|
+
const { method, values } = token.decodeInput(transaction.data)
|
|
43
|
+
|
|
44
|
+
if (method === 'transfer') {
|
|
45
|
+
return [values[0]]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return []
|
|
49
|
+
} catch {
|
|
50
|
+
return []
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { estimateSimpleTransfer, NotSimpleTransferError } from './estimate-simple-transfer.js'
|
|
2
|
+
|
|
3
|
+
export const tryEstimatingChangesLocally = ({ asset, simulationResult, transaction }) => {
|
|
4
|
+
if (typeof transaction.from !== 'string' || typeof transaction.to !== 'string') {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const estimatedValue = estimateSimpleTransfer({ baseAsset: asset, transaction })
|
|
10
|
+
const actualTransferValue =
|
|
11
|
+
transaction.from.toLowerCase() === transaction.to.toLowerCase()
|
|
12
|
+
? asset.currency.ZERO
|
|
13
|
+
: estimatedValue
|
|
14
|
+
simulationResult.balanceChanges.willSend.push({
|
|
15
|
+
balance: actualTransferValue,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return true
|
|
19
|
+
} catch (err) {
|
|
20
|
+
if (!(err instanceof NotSimpleTransferError)) {
|
|
21
|
+
// TODO: accept a logger dep and log the unexpected local simulation error.
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
export const stakingApiFactory = ({
|
|
4
|
+
assetName,
|
|
5
|
+
currency,
|
|
6
|
+
assetClientInterface,
|
|
7
|
+
server, // coin node server
|
|
8
|
+
stakingConfiguration,
|
|
9
|
+
stakingDependencies,
|
|
10
|
+
createRpcRequestAccumulator,
|
|
11
|
+
}) => {
|
|
12
|
+
assert(assetName, '"assetName" is required')
|
|
13
|
+
assert(currency, '"currency" is required')
|
|
14
|
+
assert(assetClientInterface, '"assetClientInterface" is required')
|
|
15
|
+
assert(server, '"server" is required')
|
|
16
|
+
assert(stakingConfiguration, '"stakingConfiguration" is required')
|
|
17
|
+
assert(stakingDependencies, '"stakingDependencies" is required')
|
|
18
|
+
|
|
19
|
+
const { contracts, minAmount } = stakingConfiguration
|
|
20
|
+
const { stakingServerFactory, stakingServiceFactory } = stakingDependencies
|
|
21
|
+
|
|
22
|
+
const stakingServer = stakingServerFactory({
|
|
23
|
+
assetName,
|
|
24
|
+
currency,
|
|
25
|
+
server,
|
|
26
|
+
minAmount,
|
|
27
|
+
contracts,
|
|
28
|
+
createRpcRequestAccumulator,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const stakingService = stakingServiceFactory({
|
|
32
|
+
assetClientInterface,
|
|
33
|
+
server,
|
|
34
|
+
stakingServer,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
...stakingService,
|
|
39
|
+
isStaking: async ({ isDelegating }) => isDelegating,
|
|
40
|
+
isUnstaking: async ({ isUndelegateInProgress }) => isUndelegateInProgress,
|
|
41
|
+
isUnstaked: async ({ canClaimUndelegateBalance }) => canClaimUndelegateBalance,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Background-only caching. Callers outside the monitor always request fresh data.
|
|
2
|
+
// Keep this state in memory: restored accounts must be discovered again immediately.
|
|
3
|
+
export const createCachedStakingRead = ({
|
|
4
|
+
read,
|
|
5
|
+
interval = 15 * 60_000,
|
|
6
|
+
now = () => performance.now(),
|
|
7
|
+
}) => {
|
|
8
|
+
const entries = new Map()
|
|
9
|
+
return async (key, { useCache = false } = {}) => {
|
|
10
|
+
let entry = entries.get(key)
|
|
11
|
+
if (!entry) {
|
|
12
|
+
entry = { generation: 0 }
|
|
13
|
+
entries.set(key, entry)
|
|
14
|
+
}
|
|
15
|
+
if (useCache) {
|
|
16
|
+
if (entry.pending) return entry.pending
|
|
17
|
+
if (entry.deadline > now()) return entry.value
|
|
18
|
+
}
|
|
19
|
+
const generation = ++entry.generation
|
|
20
|
+
const pending = Promise.resolve().then(() => read(key))
|
|
21
|
+
entry.pending = pending
|
|
22
|
+
try {
|
|
23
|
+
const value = await pending
|
|
24
|
+
if (entry.generation === generation) {
|
|
25
|
+
entry.value = value
|
|
26
|
+
entry.deadline = now() + interval
|
|
27
|
+
}
|
|
28
|
+
return value
|
|
29
|
+
} finally {
|
|
30
|
+
if (entry.generation === generation) entry.pending = undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|