@w3lib/evm-batch-call 0.4.1 → 0.5.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 +21 -0
- package/README.md +166 -3
- package/dist/aggregate.d.ts.map +1 -1
- package/dist/aggregate.js +153 -40
- package/dist/aggregate.js.map +1 -1
- package/dist/fallback.d.ts +20 -0
- package/dist/fallback.d.ts.map +1 -1
- package/dist/fallback.js +36 -5
- package/dist/fallback.js.map +1 -1
- package/dist/lp.d.ts +149 -0
- package/dist/lp.d.ts.map +1 -0
- package/dist/lp.js +499 -0
- package/dist/lp.js.map +1 -0
- package/dist/lpMath.d.ts +25 -0
- package/dist/lpMath.d.ts.map +1 -0
- package/dist/lpMath.js +103 -0
- package/dist/lpMath.js.map +1 -0
- package/dist/lpV4.d.ts +108 -0
- package/dist/lpV4.d.ts.map +1 -0
- package/dist/lpV4.js +283 -0
- package/dist/lpV4.js.map +1 -0
- package/dist/owner.d.ts +1 -1
- package/dist/owner.d.ts.map +1 -1
- package/dist/owner.js +18 -5
- package/dist/owner.js.map +1 -1
- package/dist/scan.d.ts +54 -0
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +124 -92
- package/dist/scan.js.map +1 -1
- package/dist/source.d.ts +2 -0
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +4 -0
- package/dist/source.js.map +1 -1
- package/dist/subpaths/lp.d.ts +33 -0
- package/dist/subpaths/lp.d.ts.map +1 -0
- package/dist/subpaths/lp.js +47 -0
- package/dist/subpaths/lp.js.map +1 -0
- package/dist/subpaths/write.d.ts +20 -0
- package/dist/subpaths/write.d.ts.map +1 -0
- package/dist/subpaths/write.js +18 -0
- package/dist/subpaths/write.js.map +1 -0
- package/dist/tron.d.ts +2 -0
- package/dist/tron.d.ts.map +1 -1
- package/dist/tron.js +4 -0
- package/dist/tron.js.map +1 -1
- package/dist/write/batch.d.ts +88 -0
- package/dist/write/batch.d.ts.map +1 -0
- package/dist/write/batch.js +231 -0
- package/dist/write/batch.js.map +1 -0
- package/dist/write/collect.d.ts +53 -0
- package/dist/write/collect.d.ts.map +1 -0
- package/dist/write/collect.js +255 -0
- package/dist/write/collect.js.map +1 -0
- package/dist/write/rpc.d.ts +68 -0
- package/dist/write/rpc.d.ts.map +1 -0
- package/dist/write/rpc.js +236 -0
- package/dist/write/rpc.js.map +1 -0
- package/dist/write/send.d.ts +124 -0
- package/dist/write/send.d.ts.map +1 -0
- package/dist/write/send.js +373 -0
- package/dist/write/send.js.map +1 -0
- package/dist/write/signer.d.ts +58 -0
- package/dist/write/signer.d.ts.map +1 -0
- package/dist/write/signer.js +41 -0
- package/dist/write/signer.js.map +1 -0
- package/dist/write/transfer.d.ts +83 -0
- package/dist/write/transfer.d.ts.map +1 -0
- package/dist/write/transfer.js +259 -0
- package/dist/write/transfer.js.map +1 -0
- package/dist/write/tron.d.ts +58 -0
- package/dist/write/tron.d.ts.map +1 -0
- package/dist/write/tron.js +956 -0
- package/dist/write/tron.js.map +1 -0
- package/package.json +27 -14
- package/src/aggregate.ts +168 -36
- package/src/fallback.ts +55 -11
- package/src/lp.ts +721 -0
- package/src/lpMath.ts +113 -0
- package/src/lpV4.ts +406 -0
- package/src/owner.ts +19 -6
- package/src/scan.ts +155 -86
- package/src/source.ts +5 -0
- package/src/subpaths/lp.ts +71 -0
- package/src/subpaths/write.ts +26 -0
- package/src/tron.ts +5 -0
- package/src/write/batch.ts +302 -0
- package/src/write/collect.ts +312 -0
- package/src/write/rpc.ts +277 -0
- package/src/write/send.ts +490 -0
- package/src/write/signer.ts +90 -0
- package/src/write/transfer.ts +344 -0
- package/src/write/tron.ts +1100 -0
package/src/scan.ts
CHANGED
|
@@ -165,6 +165,139 @@ async function loadState(storage: TransferScanStorage, key: string): Promise<Tra
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
/** 通用的增量扫描进度(transferScan 存储时把 items 存成 tokens) */
|
|
169
|
+
export interface LogScanProgress {
|
|
170
|
+
startBlock: number
|
|
171
|
+
cursor: number
|
|
172
|
+
/** 扫描中收集到的条目(去重) */
|
|
173
|
+
items: string[]
|
|
174
|
+
blockRange: number
|
|
175
|
+
rangeLimit?: number
|
|
176
|
+
rangeLimitAt?: number
|
|
177
|
+
pausedUntil?: number
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** 能查日志的节点(Provider 满足) */
|
|
181
|
+
export interface LogScanProvider {
|
|
182
|
+
getBlockNumber(): Promise<number>
|
|
183
|
+
getLogs(filter: { address?: string; fromBlock: number; toBlock: number; topics: Array<string | null> }): Promise<Array<{ address: string; topics: readonly string[] }>>
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface LogScanRun {
|
|
187
|
+
provider: LogScanProvider
|
|
188
|
+
/** 存储里读出的进度(没有时从头开始) */
|
|
189
|
+
saved: LogScanProgress | undefined
|
|
190
|
+
/** 第一次扫描时的起点:返回 cursor(已扫到的区块,下一次从 cursor + 1 开始),参数是本次的目标区块 */
|
|
191
|
+
initialCursor(target: number): number
|
|
192
|
+
filter: { address?: string; topics: Array<string | null> }
|
|
193
|
+
/** 从一条日志里取出要收集的条目,不需要的返回 null */
|
|
194
|
+
collect(log: { address: string; topics: readonly string[] }): string | null
|
|
195
|
+
maxRange: number
|
|
196
|
+
maxRequests: number
|
|
197
|
+
timeBudget: number
|
|
198
|
+
confirmations: number
|
|
199
|
+
/** 已取消时不再发新的 getLogs(已扫到的进度照常返回) */
|
|
200
|
+
signal?: AbortSignal
|
|
201
|
+
/** 本次扫描结束时调用:cursor 是已扫到的区块,target 是本次的目标区块(cursor ≥ target 表示已扫到最新) */
|
|
202
|
+
onDone?(state: LogScanProgress, target: number): void
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 增量扫描日志(transferScan、LP 仓位的 V4 扫描共用):从上次的位置往后扫到最新区块前 confirmations 个区块,
|
|
207
|
+
* 区块范围按节点的限制自动调整,限频 / 超时退避重试,节点不开放 getLogs 时暂停一段时间。
|
|
208
|
+
* 返回新的进度;拿不到最新区块且没有旧进度时返回 null
|
|
209
|
+
*/
|
|
210
|
+
export async function runLogScan(run: LogScanRun): Promise<LogScanProgress | null> {
|
|
211
|
+
const { provider, saved, filter, collect, maxRange, maxRequests, timeBudget, confirmations } = run
|
|
212
|
+
if (saved?.pausedUntil && Date.now() < saved.pausedUntil) {
|
|
213
|
+
return saved
|
|
214
|
+
}
|
|
215
|
+
const startedAt = Date.now()
|
|
216
|
+
let target: number
|
|
217
|
+
try {
|
|
218
|
+
target = (await provider.getBlockNumber()) - confirmations
|
|
219
|
+
} catch {
|
|
220
|
+
return saved ?? null // 节点不支持 / 暂时不可用:返回已有的进度
|
|
221
|
+
}
|
|
222
|
+
const now = Date.now()
|
|
223
|
+
let state: LogScanProgress
|
|
224
|
+
if (saved) {
|
|
225
|
+
state = { ...saved, items: [...saved.items] }
|
|
226
|
+
delete state.pausedUntil
|
|
227
|
+
if (state.rangeLimitAt !== undefined && now - state.rangeLimitAt > RANGE_LIMIT_TTL) {
|
|
228
|
+
delete state.rangeLimit
|
|
229
|
+
delete state.rangeLimitAt
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
const cursor = Math.max(-1, Math.min(target, run.initialCursor(target)))
|
|
233
|
+
state = { startBlock: cursor + 1, cursor, items: [], blockRange: maxRange }
|
|
234
|
+
}
|
|
235
|
+
const items = new Set(state.items)
|
|
236
|
+
const cap = () => Math.min(maxRange, state.rangeLimit ?? maxRange)
|
|
237
|
+
const setLimit = (limit: number) => {
|
|
238
|
+
state.rangeLimit = limit
|
|
239
|
+
state.rangeLimitAt = Date.now()
|
|
240
|
+
}
|
|
241
|
+
let range = Math.min(state.blockRange, cap())
|
|
242
|
+
let successes = 0
|
|
243
|
+
let transient = 0
|
|
244
|
+
|
|
245
|
+
for (let requests = 0; requests < maxRequests && state.cursor < target; requests++) {
|
|
246
|
+
if ((timeBudget > 0 && Date.now() - startedAt >= timeBudget) || run.signal?.aborted) {
|
|
247
|
+
break // 超出时间预算 / 已取消,留到下次调用
|
|
248
|
+
}
|
|
249
|
+
const fromBlock = state.cursor + 1
|
|
250
|
+
const toBlock = Math.min(target, fromBlock + range - 1)
|
|
251
|
+
try {
|
|
252
|
+
const logs = await provider.getLogs({ ...(filter.address ? { address: filter.address } : {}), fromBlock, toBlock, topics: filter.topics })
|
|
253
|
+
for (const log of logs) {
|
|
254
|
+
const item = collect(log)
|
|
255
|
+
if (item !== null) {
|
|
256
|
+
items.add(item)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
state.cursor = toBlock
|
|
260
|
+
transient = 0
|
|
261
|
+
if (++successes >= GROW_AFTER && range < cap()) {
|
|
262
|
+
range = Math.min(cap(), range * 2) // 之前缩小过(偶发错误 / 换了节点):逐步放大回去
|
|
263
|
+
successes = 0
|
|
264
|
+
}
|
|
265
|
+
} catch (err) {
|
|
266
|
+
successes = 0
|
|
267
|
+
const texts = errorTexts(err)
|
|
268
|
+
const kinds = texts.map(kindOf)
|
|
269
|
+
// 上次成功的节点这次的错误最能说明问题(其他节点可能一直就不能用,它们的报错不代表当前范围有问题)
|
|
270
|
+
const preferred = err instanceof GetLogsError && err.preferred !== undefined ? kindOf(errorText(err.preferred)) : undefined
|
|
271
|
+
const limit = parseRangeLimit(texts)
|
|
272
|
+
if (limit !== null && limit < range) {
|
|
273
|
+
range = limit // 节点明确给了范围上限
|
|
274
|
+
setLimit(limit)
|
|
275
|
+
} else if (kinds.every((k) => k === 'unsupported')) {
|
|
276
|
+
// 所有节点都不开放 getLogs:暂停一段时间,避免每次调用都白发请求
|
|
277
|
+
state.pausedUntil = Date.now() + PAUSE_ON_UNSUPPORTED
|
|
278
|
+
break
|
|
279
|
+
} else if (preferred === 'transient' || (preferred === undefined && kinds.every((k) => k === 'transient' || k === 'unsupported'))) {
|
|
280
|
+
// 在用的节点只是限频 / 超时:不缩小范围,退避后重试;连续 3 次就留到下次调用
|
|
281
|
+
const remaining = timeBudget > 0 ? timeBudget - (Date.now() - startedAt) : Infinity
|
|
282
|
+
if (++transient >= 3 || remaining <= 0) {
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
await sleep(Math.min(500 * transient, remaining))
|
|
286
|
+
} else if (range > 1) {
|
|
287
|
+
range = Math.max(1, Math.floor(range / 2)) // 范围超限但没说上限:减半再试
|
|
288
|
+
setLimit(range)
|
|
289
|
+
} else {
|
|
290
|
+
state.pausedUntil = Date.now() + PAUSE_ON_UNSUPPORTED // 连 1 个区块都查不了
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
state.blockRange = range
|
|
296
|
+
state.items = [...items]
|
|
297
|
+
run.onDone?.(state, target)
|
|
298
|
+
return state
|
|
299
|
+
}
|
|
300
|
+
|
|
168
301
|
/**
|
|
169
302
|
* 增量扫描地址收到的 ERC20 代币(Transfer 事件,to = 该地址):第一次调用记下当前区块,之后每次从上次的位置往后扫。
|
|
170
303
|
* 用来补充代币列表发现不了的新代币。作为代币来源使用:`combine(defaultTokenSource(), transferScan())`,
|
|
@@ -188,100 +321,36 @@ export function transferScan(options: TransferScanOptions = {}): TokenSource {
|
|
|
188
321
|
}
|
|
189
322
|
const key = `${chainId}:${owner.toLowerCase()}`
|
|
190
323
|
const saved = await loadState(storage, key)
|
|
191
|
-
const toResult = (
|
|
192
|
-
if (saved?.pausedUntil && Date.now() < saved.pausedUntil) {
|
|
193
|
-
return toResult(saved)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const startedAt = Date.now()
|
|
197
|
-
let target: number
|
|
198
|
-
try {
|
|
199
|
-
target = (await provider.getBlockNumber()) - confirmations
|
|
200
|
-
} catch {
|
|
201
|
-
return saved ? toResult(saved) : null // 节点不支持 / 暂时不可用:返回已发现的代币
|
|
202
|
-
}
|
|
203
|
-
const now = Date.now()
|
|
204
|
-
let state: TransferScanState
|
|
205
|
-
if (saved) {
|
|
206
|
-
state = { ...saved, tokens: [...saved.tokens] }
|
|
207
|
-
delete state.pausedUntil
|
|
208
|
-
if (state.rangeLimitAt !== undefined && now - state.rangeLimitAt > RANGE_LIMIT_TTL) {
|
|
209
|
-
delete state.rangeLimit
|
|
210
|
-
delete state.rangeLimitAt
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
const cursor = Math.max(-1, target - lookback)
|
|
214
|
-
state = { startBlock: cursor + 1, cursor, tokens: [], blockRange: maxRange }
|
|
215
|
-
}
|
|
216
|
-
const tokens = new Set(state.tokens)
|
|
324
|
+
const toResult = (tokens: readonly string[]): DiscoveredToken[] => tokens.map((address) => ({ address }))
|
|
217
325
|
const ownerTopic = `0x${owner.slice(2).toLowerCase().padStart(64, '0')}`
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
326
|
+
const savedProgress = saved && { ...saved, items: saved.tokens }
|
|
327
|
+
const progress = await runLogScan({
|
|
328
|
+
provider,
|
|
329
|
+
saved: savedProgress,
|
|
330
|
+
initialCursor: (target) => target - lookback,
|
|
331
|
+
filter: { topics: [TRANSFER_TOPIC, null, ownerTopic] },
|
|
332
|
+
// ERC20 的 Transfer 有 3 个 topic;ERC721 的 tokenId 也是 indexed,有 4 个,排除
|
|
333
|
+
collect: (log) => (log.topics.length === 3 ? log.address.toLowerCase() : null),
|
|
334
|
+
maxRange,
|
|
335
|
+
maxRequests,
|
|
336
|
+
timeBudget,
|
|
337
|
+
confirmations,
|
|
338
|
+
})
|
|
339
|
+
if (!progress) {
|
|
340
|
+
return null
|
|
222
341
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
for (let requests = 0; requests < maxRequests && state.cursor < target; requests++) {
|
|
228
|
-
if (timeBudget > 0 && Date.now() - startedAt >= timeBudget) {
|
|
229
|
-
break // 超出时间预算,留到下次调用
|
|
230
|
-
}
|
|
231
|
-
const fromBlock = state.cursor + 1
|
|
232
|
-
const toBlock = Math.min(target, fromBlock + range - 1)
|
|
233
|
-
try {
|
|
234
|
-
const logs = await provider.getLogs({ fromBlock, toBlock, topics: [TRANSFER_TOPIC, null, ownerTopic] })
|
|
235
|
-
for (const log of logs) {
|
|
236
|
-
// ERC20 的 Transfer 有 3 个 topic;ERC721 的 tokenId 也是 indexed,有 4 个,排除
|
|
237
|
-
if (log.topics.length === 3) {
|
|
238
|
-
tokens.add(log.address.toLowerCase())
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
state.cursor = toBlock
|
|
242
|
-
transient = 0
|
|
243
|
-
if (++successes >= GROW_AFTER && range < cap()) {
|
|
244
|
-
range = Math.min(cap(), range * 2) // 之前缩小过(偶发错误 / 换了节点):逐步放大回去
|
|
245
|
-
successes = 0
|
|
246
|
-
}
|
|
247
|
-
} catch (err) {
|
|
248
|
-
successes = 0
|
|
249
|
-
const texts = errorTexts(err)
|
|
250
|
-
const kinds = texts.map(kindOf)
|
|
251
|
-
// 上次成功的节点这次的错误最能说明问题(其他节点可能一直就不能用,它们的报错不代表当前范围有问题)
|
|
252
|
-
const preferred = err instanceof GetLogsError && err.preferred !== undefined ? kindOf(errorText(err.preferred)) : undefined
|
|
253
|
-
const limit = parseRangeLimit(texts)
|
|
254
|
-
if (limit !== null && limit < range) {
|
|
255
|
-
range = limit // 节点明确给了范围上限
|
|
256
|
-
setLimit(limit)
|
|
257
|
-
} else if (kinds.every((k) => k === 'unsupported')) {
|
|
258
|
-
// 所有节点都不开放 getLogs:暂停一段时间,避免每次调用都白发请求
|
|
259
|
-
state.pausedUntil = Date.now() + PAUSE_ON_UNSUPPORTED
|
|
260
|
-
break
|
|
261
|
-
} else if (preferred === 'transient' || (preferred === undefined && kinds.every((k) => k === 'transient' || k === 'unsupported'))) {
|
|
262
|
-
// 在用的节点只是限频 / 超时:不缩小范围,退避后重试;连续 3 次就留到下次调用
|
|
263
|
-
const remaining = timeBudget > 0 ? timeBudget - (Date.now() - startedAt) : Infinity
|
|
264
|
-
if (++transient >= 3 || remaining <= 0) {
|
|
265
|
-
break
|
|
266
|
-
}
|
|
267
|
-
await sleep(Math.min(500 * transient, remaining))
|
|
268
|
-
} else if (range > 1) {
|
|
269
|
-
range = Math.max(1, Math.floor(range / 2)) // 范围超限但没说上限:减半再试
|
|
270
|
-
setLimit(range)
|
|
271
|
-
} else {
|
|
272
|
-
state.pausedUntil = Date.now() + PAUSE_ON_UNSUPPORTED // 连 1 个区块都查不了
|
|
273
|
-
break
|
|
274
|
-
}
|
|
275
|
-
}
|
|
342
|
+
// 暂停中 / 拿不到最新区块:进度没变,不用写回
|
|
343
|
+
if (progress === savedProgress) {
|
|
344
|
+
return toResult(progress.items)
|
|
276
345
|
}
|
|
277
|
-
|
|
278
|
-
state
|
|
346
|
+
const { items, ...rest } = progress
|
|
347
|
+
const state: TransferScanState = { ...rest, tokens: items }
|
|
279
348
|
try {
|
|
280
349
|
await storage.set(key, state)
|
|
281
350
|
} catch {
|
|
282
351
|
// 存储不可用:只影响下次能否接着扫
|
|
283
352
|
}
|
|
284
|
-
return toResult(state)
|
|
353
|
+
return toResult(state.tokens)
|
|
285
354
|
},
|
|
286
355
|
}
|
|
287
356
|
}
|
package/src/source.ts
CHANGED
|
@@ -143,6 +143,11 @@ export class ChainCheckedProvider implements EthersLikeProvider {
|
|
|
143
143
|
return this.#run(() => this.inner.call(tx))
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** 对内层节点做任意请求,同样先校验链(写操作模块用) */
|
|
147
|
+
request<T>(fn: (inner: EthersLikeProvider) => Promise<T>): Promise<T> {
|
|
148
|
+
return this.#run(() => fn(this.inner))
|
|
149
|
+
}
|
|
150
|
+
|
|
146
151
|
getBalance(...args: Parameters<EthersLikeProvider['getBalance']>): Promise<bigint> {
|
|
147
152
|
return this.#run(() => this.inner.getBalance(...args))
|
|
148
153
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LP 仓位(子路径 `@w3lib/evm-batch-call/lp`):查一个地址在各 DEX 的流动性仓位——V2 的 LP 代币、V3 / V4 的仓位 NFT,
|
|
3
|
+
* 代币数量、未领取的手续费、可选的美元价值。不放在主入口,不用时打包不会带上这部分代码。
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { getLpPositions } from '@w3lib/evm-batch-call/lp'
|
|
7
|
+
*
|
|
8
|
+
* await getLpPositions(user, { chainId: 56 }) // 节点参数与 getBalances 相同
|
|
9
|
+
* await getLpPositions(user, { chainId: 56, prices: true }) // 带美元价值(DefiLlama)
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { lpPositions, type LpPosition, type LpPositionsOptions } from '../lp.js'
|
|
13
|
+
import { resolve, type ShortcutProviderOptions } from '../shortcuts.js'
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
LP_PROTOCOLS,
|
|
17
|
+
LP_QUOTE_TOKENS,
|
|
18
|
+
lpPositions,
|
|
19
|
+
resolveLpProtocols,
|
|
20
|
+
type LpConcentratedPosition,
|
|
21
|
+
type LpPosition,
|
|
22
|
+
type LpPositionsOptions,
|
|
23
|
+
type LpProtocol,
|
|
24
|
+
type LpToken,
|
|
25
|
+
type LpV2Position,
|
|
26
|
+
type LpVersion,
|
|
27
|
+
} from '../lp.js'
|
|
28
|
+
export {
|
|
29
|
+
decodePositionInfo,
|
|
30
|
+
keyValueLpScanStorage,
|
|
31
|
+
memoryLpScanStorage,
|
|
32
|
+
poolIdOf,
|
|
33
|
+
type LpScanStorage,
|
|
34
|
+
type LpV4Options,
|
|
35
|
+
type V4ScanOptions,
|
|
36
|
+
type V4ScanProgress,
|
|
37
|
+
} from '../lpV4.js'
|
|
38
|
+
export { MAX_TICK, MIN_TICK, getAmountsForLiquidity, getFeeGrowthInside, getFeesEarned, getSqrtRatioAtTick, sqrtPriceToPrice, tickToPrice } from '../lpMath.js'
|
|
39
|
+
export { defillamaPrices, type PriceSource } from '../owner.js'
|
|
40
|
+
|
|
41
|
+
export interface GetLpPositionsOptions extends LpPositionsOptions, ShortcutProviderOptions {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 查一个地址的 LP 仓位(V2 LP 代币、V3 仓位 NFT;V4 仓位 NFT 需要传 v4.tokenIds 或开启 v4.scan),所有合约读取用 multicall 批量完成。
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* await getLpPositions(user, { chainId: 56 })
|
|
48
|
+
* // [{ version: 'v3', protocol: 'pancakeswap-v3', pool, tokenId: '123', token0: { symbol: 'WBNB', … }, token1: { symbol: 'USDT', … },
|
|
49
|
+
* // amount0Formatted: '1.2', amount1Formatted: '700', fees0Formatted: '0.003', fees1Formatted: '1.8', inRange: true, … },
|
|
50
|
+
* // { version: 'v2', protocol: 'pancakeswap-v2', pool, balance, share: 0.0001, amount0Formatted, amount1Formatted, … }]
|
|
51
|
+
* await getLpPositions(user, { chainId: 56, tokens: heldTokens }) // V2:用持有的代币 × 计价币找池子
|
|
52
|
+
* await getLpPositions(user, { chainId: 56, prices: true }) // 带美元价值,按价值排序
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function getLpPositions(owner: string, options: GetLpPositionsOptions = {}): Promise<LpPosition[]> {
|
|
56
|
+
const { provider, overrides, own } = resolve(options, [
|
|
57
|
+
'protocols',
|
|
58
|
+
'versions',
|
|
59
|
+
'pools',
|
|
60
|
+
'tokens',
|
|
61
|
+
'quoteTokens',
|
|
62
|
+
'source',
|
|
63
|
+
'includeClosed',
|
|
64
|
+
'maxPositions',
|
|
65
|
+
'v4',
|
|
66
|
+
'prices',
|
|
67
|
+
'onPriceError',
|
|
68
|
+
'fetch',
|
|
69
|
+
])
|
|
70
|
+
return lpPositions(provider, owner, { ...overrides, ...own })
|
|
71
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 批量写操作(子路径 `@w3lib/evm-batch-call/write`):批量调用合约、一对多转账、多钱包归集。
|
|
3
|
+
* 不放在主入口,只做查询时打包不会带上这部分代码。
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { Provider, NATIVE_TOKEN } from '@w3lib/evm-batch-call'
|
|
7
|
+
* import { batchCall, batchCollect, batchTransfer } from '@w3lib/evm-batch-call/write'
|
|
8
|
+
*
|
|
9
|
+
* const multi = new Provider(56, rpcUrl)
|
|
10
|
+
* await batchTransfer(multi, privateKey, [{ to: a, formatted: '1.5' }, { to: b, formatted: '2' }], { token: USDT })
|
|
11
|
+
* await batchCollect(multi, [key1, key2], hotWallet, { tokens: [USDT, NATIVE_TOKEN], gasWallet: gasKey })
|
|
12
|
+
* await batchCall(multi, [{ from: privateKey, call: usdt.approve(router, amount) }, { from: privateKey, to: router, data }])
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export {
|
|
16
|
+
SIMPLE_7702_ACCOUNT,
|
|
17
|
+
batchCall,
|
|
18
|
+
revokeDelegation,
|
|
19
|
+
type BatchCallMode,
|
|
20
|
+
type BatchCallOptions,
|
|
21
|
+
type WriteCall,
|
|
22
|
+
} from '../write/batch.js'
|
|
23
|
+
export { batchCollect, type BatchCollectOptions, type CollectItemResult, type CollectResult } from '../write/collect.js'
|
|
24
|
+
export { DISPERSE_ADDRESS, batchTransfer, type ApproveResult, type BatchTransferOptions, type TransferItem, type TransferMode, type TransferResult } from '../write/transfer.js'
|
|
25
|
+
export type { SendOptions, TronSendOptions, WriteResult, WriteStatus } from '../write/send.js'
|
|
26
|
+
export type { SignerInput, TronSigner, TronWalletLike, WriteSigner } from '../write/signer.js'
|
package/src/tron.ts
CHANGED
|
@@ -169,6 +169,11 @@ export class TronProvider implements EthersLikeProvider {
|
|
|
169
169
|
})
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/** 直接请求 HTTP API(如 'wallet/getnowblock'),同样经过限流和 429 退避(写操作模块用) */
|
|
173
|
+
request(path: string, body: Record<string, unknown> = {}): Promise<any> {
|
|
174
|
+
return this.#limit(() => this.#request(path, body))
|
|
175
|
+
}
|
|
176
|
+
|
|
172
177
|
async call(tx: TransactionRequest): Promise<string> {
|
|
173
178
|
assertLatest(tx.blockTag)
|
|
174
179
|
const body: Record<string, unknown> = {
|