@valve-tech/gas-oracle 0.6.0 → 0.8.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 +54 -0
- package/README.md +115 -16
- package/dist/block-position.d.ts +3 -3
- package/dist/block-position.d.ts.map +1 -1
- package/dist/block-position.js +32 -20
- package/dist/block-position.js.map +1 -1
- package/dist/classify-tip.d.ts +33 -0
- package/dist/classify-tip.d.ts.map +1 -0
- package/dist/classify-tip.js +52 -0
- package/dist/classify-tip.js.map +1 -0
- package/dist/inclusion-labels.d.ts +30 -0
- package/dist/inclusion-labels.d.ts.map +1 -0
- package/dist/inclusion-labels.js +35 -0
- package/dist/inclusion-labels.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +20 -18
- package/dist/math.js.map +1 -1
- package/dist/oracle.d.ts +2 -2
- package/dist/oracle.d.ts.map +1 -1
- package/dist/oracle.js +27 -11
- package/dist/oracle.js.map +1 -1
- package/dist/presets.d.ts +36 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +27 -0
- package/dist/presets.js.map +1 -0
- package/dist/replacement.d.ts +80 -0
- package/dist/replacement.d.ts.map +1 -0
- package/dist/replacement.js +114 -0
- package/dist/replacement.js.map +1 -0
- package/dist/types.d.ts +49 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +57 -1
- package/dist/types.js.map +1 -1
- package/dist/viem-transport.d.ts +1 -1
- package/dist/viem-transport.d.ts.map +1 -1
- package/dist/viem-transport.js +11 -5
- package/dist/viem-transport.js.map +1 -1
- package/package.json +2 -2
- package/skills/gas-oracle-integration/SKILL.md +159 -13
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,60 @@ All notable changes to `@valve-tech/gas-oracle` are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.8.0] — 2026-05-06
|
|
9
|
+
|
|
10
|
+
Consumes `@valve-tech/chain-source@0.8.0`'s WS-aware `ChainSource` transparently. Adds upstream helpers (replacement / classifyTip / inclusion labels), chain presets (PulseChain), const-namespace exports, and a bigint migration of public numeric fields.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Replacement helpers** (`replacement.ts`): `minimumReplacementFee(current, txType)`, `bumpForReplacement(currentGas, targetGas)`, `recommendBumpTier(snapshot, stuckTx, options?)` with three named strategies (`BumpStrategy.cheapestThatLands` / `oneStepFasterThanRecommended` / `instant`). Outpace correction via optional `stuckTx.identifier` reads `snapshot.mempoolSamples` to find the tip needed to outpace the stuck tx in the live distribution, on top of the EIP-1559 +10% protocol floor (verified against geth/reth/PulseChain go-pulse sources).
|
|
15
|
+
- **`classifyTip(snapshot, tipWei)`** (`classify-tip.ts`): inverse of `tipForBlockPosition`. Returns `{ tier, requiredForNextTier, percentile, rank, gasFromTop }`.
|
|
16
|
+
- **Inclusion labels** (`inclusion-labels.ts`): `defaultInclusionLabels` (Record<TierName, string>) and `inclusionLabel(tier, overrides?)` for locale/branded copy without forking.
|
|
17
|
+
- **Chain presets** (`presets.ts`): `chainPresets.pulsechain` (chainId: 369, priorityModel: PriorityModel.flat) plus `presetForChainId(chainId)` runtime lookup.
|
|
18
|
+
- **Const-namespace exports**: `PriorityModel`, `TierName`, `Trend`, `TxType` are now exported as both values (const namespaces) and types. Use `PriorityModel.flat` instead of `'flat'` etc. `TIER_LADDER` (canonical slow→instant ordering) also exported from `types.ts`.
|
|
19
|
+
- **`mempoolSamples: TipSample[]`** on `GasOracleState`. Producer-local — wire publishers should strip before serializing (same convention as `ring`).
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`priorityModel` default flips from `'flat'` to `PriorityModel.eip1559`**. Most chains honor the EIP-2718 type byte and the EIP-1559 fee-market; the default is now correct for the majority case. PulseChain (chain 369) becomes the explicit exception — set `priorityModel: PriorityModel.flat` (or use `...chainPresets.pulsechain`).
|
|
24
|
+
- **BigInt migration of public numeric fields**: `MempoolStats.pendingCount`, `MempoolStats.queuedCount`, `BlockPositionQuery.rank`, `BlockPositionQuery.percentile`, `BlockPositionResult.rank`, `CreateGasOracleOptions.pollIntervalMs` are now `bigint`. Identifier-like fields (`chainId`, EIP-2718 type bytes) stay `number`.
|
|
25
|
+
|
|
26
|
+
### Migration notes
|
|
27
|
+
|
|
28
|
+
- Examples that previously omitted `priorityModel` silently get `PriorityModel.eip1559` after upgrade. Verify against your target chain.
|
|
29
|
+
- Math operations on the migrated bigint fields need `n` literals (`0n`, `1n`, etc.) and integer-floor division semantics.
|
|
30
|
+
- `pollIntervalMs: 5000` becomes `pollIntervalMs: 5000n`.
|
|
31
|
+
|
|
32
|
+
## [0.7.0] — 2026-05-06
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **`skills/gas-oracle-integration/SKILL.md`** — appended a "Tx
|
|
37
|
+
tracking — composing with `@valve-tech/tx-tracker`" section that
|
|
38
|
+
redirects per-tx tracking questions to the sibling package and
|
|
39
|
+
documents the shared-`ChainSource` composition pattern. The skill
|
|
40
|
+
`description` trigger phrases now also catch composition questions,
|
|
41
|
+
but per-tx work explicitly defers to the tx-tracker skill. Ships
|
|
42
|
+
in the npm tarball.
|
|
43
|
+
- **Coverage hardening pre-1.0.** Test suite up from 189 → 209 tests
|
|
44
|
+
(+20). New coverage: `sampleGasFees` one-shot snapshot path
|
|
45
|
+
(success + null + error-routing variants), `tipForBlockPosition`
|
|
46
|
+
with full mempool data (EIP-1559 + legacy + 0-headroom + no-gas
|
|
47
|
+
+ no-fee branches in the mempool→TipSample translation),
|
|
48
|
+
`formatTier` for tx types 0/1/2/3 plus the no-type fallback —
|
|
49
|
+
with and without blob fees in tier state, `keepMempoolSnapshot:
|
|
50
|
+
true` retention, `pauseWhenIdle` re-subscribe inside the stale
|
|
51
|
+
window keeping the loop alive, `fetchHeadBlockNumber` happy /
|
|
52
|
+
null-from-RPC / throw / un-decodable variants. Eliminated dead
|
|
53
|
+
defensive arms in `math.ts` / `block-position.ts` sort
|
|
54
|
+
comparators (equal-key arms unreachable since duplicates are
|
|
55
|
+
filtered upstream) and in `oracle.ts` `attachToSource` (stale-
|
|
56
|
+
timer clear is unreachable because every detach path that sets
|
|
57
|
+
the timer also keeps `unsubBlocks !== null`, which makes
|
|
58
|
+
`attachToSource` early-return before the clear).
|
|
59
|
+
- Coverage went **91.91% / 84.75% / 93.18% / 93.9%** stmts /
|
|
60
|
+
branches / funcs / lines → **98.26% / 93.47% / 100% / 99.8%**.
|
|
61
|
+
|
|
8
62
|
## [0.6.0] — 2026-05-05
|
|
9
63
|
|
|
10
64
|
### Added
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ yarn add @valve-tech/gas-oracle viem
|
|
|
28
28
|
```ts
|
|
29
29
|
import { createPublicClient, http, parseEther } from 'viem'
|
|
30
30
|
import { mainnet } from 'viem/chains'
|
|
31
|
-
import { createGasOracle } from '@valve-tech/gas-oracle'
|
|
31
|
+
import { createGasOracle, PriorityModel } from '@valve-tech/gas-oracle'
|
|
32
32
|
|
|
33
33
|
const client = createPublicClient({ chain: mainnet, transport: http() })
|
|
34
34
|
|
|
@@ -36,7 +36,7 @@ const oracle = createGasOracle({
|
|
|
36
36
|
client,
|
|
37
37
|
chainId: 1,
|
|
38
38
|
priorityFeeDecayCap: parseEther('0.125'), // 12.5%/block, EIP-1559 parity
|
|
39
|
-
priorityModel
|
|
39
|
+
// priorityModel defaults to PriorityModel.eip1559 — explicit only if you need to override
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
oracle.subscribe((state) => {
|
|
@@ -73,7 +73,7 @@ Tier mapping in the gas-weighted percentile distribution:
|
|
|
73
73
|
|
|
74
74
|
`slow` always reads from the full distribution (legacy + 1559) so legacy
|
|
75
75
|
senders can still find the lane they actually live in. Under
|
|
76
|
-
`
|
|
76
|
+
`PriorityModel.eip1559`, the paying-lane tiers (`standard`/`fast`/
|
|
77
77
|
`instant`) draw from type-2+ samples only — legacy spam can't suppress
|
|
78
78
|
them.
|
|
79
79
|
|
|
@@ -95,19 +95,32 @@ priorityFeeDecayCap: null // uncapped — track raw mempool
|
|
|
95
95
|
Validated at construction; out-of-range values throw. Upside is always
|
|
96
96
|
unclamped — real spikes propagate immediately.
|
|
97
97
|
|
|
98
|
-
### `priorityModel`
|
|
98
|
+
### Choosing `priorityModel`
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
EIP-2718 type space:
|
|
100
|
+
The default is `PriorityModel.eip1559` and is correct for every chain whose validators honor the EIP-2718 type byte and the EIP-1559 fee-market shape. **You should only override this if you've verified your target chain's validators are extractive** — that is, they ignore the type byte and maximize fee per gas regardless of tx envelope.
|
|
102
101
|
|
|
103
|
-
|
|
102
|
+
The canonical example is **PulseChain (chain 369)**: extractive validators mean the percentile math has to draw from the full tx distribution (`PriorityModel.flat`) instead of filtering to type-2+ samples. Setting the wrong model here silently under-prices, and your tx stalls.
|
|
103
|
+
|
|
104
|
+
For chains we know about, the `chainPresets` entry-point handles this for you:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { createGasOracle, chainPresets } from '@valve-tech/gas-oracle'
|
|
108
|
+
|
|
109
|
+
const oracle = createGasOracle({
|
|
110
|
+
client,
|
|
111
|
+
...chainPresets.pulsechain, // chainId: 369, priorityModel: PriorityModel.flat
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Mechanics:
|
|
116
|
+
|
|
117
|
+
- `PriorityModel.flat` — every tx contributes equally to the gas-weighted
|
|
104
118
|
distribution. Right for extractive validators (PulseChain, etc.)
|
|
105
119
|
that ignore the type byte and just maximize fee per gas.
|
|
106
|
-
- `
|
|
107
|
-
fast/instant); `slow` still draws from the full
|
|
108
|
-
for chains that honor EIP-1559 ordering
|
|
109
|
-
|
|
110
|
-
Default `'flat'` (most conservative — never under-counts spam).
|
|
120
|
+
- `PriorityModel.eip1559` — type 2+ samples drive the paying-lane tiers
|
|
121
|
+
(standard/fast/instant); `slow` still draws from the full
|
|
122
|
+
distribution. Right for chains that honor EIP-1559 ordering
|
|
123
|
+
(Ethereum, most L2s).
|
|
111
124
|
|
|
112
125
|
### `baseFeeLivenessBlocks`
|
|
113
126
|
|
|
@@ -240,12 +253,12 @@ For callers who need a single fee snapshot without a long-lived
|
|
|
240
253
|
oracle (typical tx-submit flow):
|
|
241
254
|
|
|
242
255
|
```ts
|
|
243
|
-
import { sampleGasFees } from '@valve-tech/gas-oracle'
|
|
256
|
+
import { sampleGasFees, PriorityModel } from '@valve-tech/gas-oracle'
|
|
244
257
|
|
|
245
258
|
const snapshot = await sampleGasFees({
|
|
246
259
|
client,
|
|
247
260
|
chainId: 1,
|
|
248
|
-
priorityModel:
|
|
261
|
+
priorityModel: PriorityModel.eip1559,
|
|
249
262
|
})
|
|
250
263
|
const tip = snapshot?.tiers.fast.maxPriorityFeePerGas
|
|
251
264
|
```
|
|
@@ -309,6 +322,90 @@ the same union `computeTiers` reads. The viem-actions extension
|
|
|
309
322
|
exposes `client.tipForBlockPosition(query)` which assembles this
|
|
310
323
|
distribution for you from the oracle's state.
|
|
311
324
|
|
|
325
|
+
## Replacement workflow
|
|
326
|
+
|
|
327
|
+
When a tx gets stuck and you need to bump it past the EIP-1559 protocol
|
|
328
|
+
replacement floor (and optionally past the live mempool distribution):
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
import {
|
|
332
|
+
createGasOracle,
|
|
333
|
+
recommendBumpTier,
|
|
334
|
+
bumpForReplacement,
|
|
335
|
+
BumpStrategy,
|
|
336
|
+
} from '@valve-tech/gas-oracle'
|
|
337
|
+
|
|
338
|
+
// 1. Pick a tier to bump to:
|
|
339
|
+
const tier = recommendBumpTier(
|
|
340
|
+
state,
|
|
341
|
+
{ priorityTip: stuckTx.maxPriorityFeePerGas, identifier: { hash: stuckTx.hash } },
|
|
342
|
+
{ strategy: BumpStrategy.cheapestThatLands },
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
if (tier === null) {
|
|
346
|
+
// Stuck tx is already paying above the top of the tier ladder, or
|
|
347
|
+
// the snapshot has no tip data. Caller's call: hold, or push instant.
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// 2. Compute the gas object that satisfies both the protocol floor and the target tier:
|
|
352
|
+
const target = state.tiers[tier]
|
|
353
|
+
const gas = bumpForReplacement(
|
|
354
|
+
{ maxFeePerGas: stuckTx.maxFeePerGas, maxPriorityFeePerGas: stuckTx.maxPriorityFeePerGas },
|
|
355
|
+
{ maxFeePerGas: target.maxFeePerGas, maxPriorityFeePerGas: target.maxPriorityFeePerGas },
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
// 3. Send the replacement
|
|
359
|
+
walletClient.sendTransaction({ ...stuckTx, ...gas })
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Tip classification
|
|
363
|
+
|
|
364
|
+
Inverse of `tipForBlockPosition`: given a tip, find where it lands in the
|
|
365
|
+
live distribution and which named tier it falls in.
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
import { classifyTip } from '@valve-tech/gas-oracle'
|
|
369
|
+
|
|
370
|
+
const result = classifyTip(state, myTip)
|
|
371
|
+
// result.tier — TierName | null (null if below slow)
|
|
372
|
+
// result.requiredForNextTier — bigint floor of next tier above (null at instant)
|
|
373
|
+
// result.percentile — bigint 0-100 (0 = top, 100 = bottom)
|
|
374
|
+
// result.rank — bigint 0-indexed from top
|
|
375
|
+
// result.gasFromTop — bigint accumulated gas above this tip
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## UI labels
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
import { defaultInclusionLabels, inclusionLabel, TierName } from '@valve-tech/gas-oracle'
|
|
382
|
+
|
|
383
|
+
defaultInclusionLabels[TierName.standard] // 'Next block'
|
|
384
|
+
|
|
385
|
+
// Locale / branded copy via partial overrides — no fork:
|
|
386
|
+
const es = { [TierName.standard]: 'Próximo bloque' }
|
|
387
|
+
inclusionLabel(TierName.standard, es) // 'Próximo bloque'
|
|
388
|
+
inclusionLabel(TierName.slow, es) // falls back to default English
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Chain presets
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import { createGasOracle, chainPresets, presetForChainId } from '@valve-tech/gas-oracle'
|
|
395
|
+
|
|
396
|
+
// Static — direct preset access
|
|
397
|
+
createGasOracle({ client, ...chainPresets.pulsechain })
|
|
398
|
+
|
|
399
|
+
// Dynamic — runtime lookup by chainId
|
|
400
|
+
const preset = presetForChainId(chainId)
|
|
401
|
+
createGasOracle({ client, chainId, ...preset })
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
PulseChain (chain 369) is the only entry shipped today. Adding more
|
|
405
|
+
requires verifying the chain's actual validator behavior against
|
|
406
|
+
block-level data; the default (eip1559) is correct for every chain we
|
|
407
|
+
haven't proven otherwise.
|
|
408
|
+
|
|
312
409
|
## viem integration
|
|
313
410
|
|
|
314
411
|
### Subpath: `@valve-tech/gas-oracle/viem-actions`
|
|
@@ -317,12 +414,13 @@ Extension surface for callers who want explicit access to tier shapes:
|
|
|
317
414
|
|
|
318
415
|
```ts
|
|
319
416
|
import { gasOracleActions } from '@valve-tech/gas-oracle/viem-actions'
|
|
417
|
+
import { PriorityModel } from '@valve-tech/gas-oracle'
|
|
320
418
|
|
|
321
419
|
const client = createPublicClient({ chain: mainnet, transport: http() })
|
|
322
420
|
.extend(gasOracleActions({
|
|
323
421
|
chainId: 1,
|
|
324
422
|
priorityFeeDecayCap: parseEther('0.125'),
|
|
325
|
-
priorityModel:
|
|
423
|
+
priorityModel: PriorityModel.eip1559,
|
|
326
424
|
}))
|
|
327
425
|
|
|
328
426
|
await client.getGasTiers() // full snapshot
|
|
@@ -343,11 +441,12 @@ work better* — `useFeeData`, `walletClient.sendTransaction({...})`,
|
|
|
343
441
|
|
|
344
442
|
```ts
|
|
345
443
|
import { withGasOracle } from '@valve-tech/gas-oracle/viem-transport'
|
|
444
|
+
import { PriorityModel } from '@valve-tech/gas-oracle'
|
|
346
445
|
|
|
347
446
|
const transport = withGasOracle(http(rpcUrl), {
|
|
348
447
|
chainId: 1,
|
|
349
448
|
priorityFeeDecayCap: parseEther('0.125'),
|
|
350
|
-
priorityModel:
|
|
449
|
+
priorityModel: PriorityModel.eip1559,
|
|
351
450
|
intercept: {
|
|
352
451
|
eth_gasFeeEstimate: true, // additive (default on)
|
|
353
452
|
eth_maxPriorityFeePerGas: 'fast', // tier required for standard methods
|
package/dist/block-position.d.ts
CHANGED
|
@@ -48,10 +48,10 @@ import type { TxIdentifier } from './mempool.js';
|
|
|
48
48
|
*/
|
|
49
49
|
export type BlockPositionQuery = {
|
|
50
50
|
kind: 'rank';
|
|
51
|
-
rank:
|
|
51
|
+
rank: bigint;
|
|
52
52
|
} | {
|
|
53
53
|
kind: 'percentile';
|
|
54
|
-
percentile:
|
|
54
|
+
percentile: bigint;
|
|
55
55
|
} | {
|
|
56
56
|
kind: 'gasFromTop';
|
|
57
57
|
gas: bigint;
|
|
@@ -79,7 +79,7 @@ export interface BlockPositionResult {
|
|
|
79
79
|
*/
|
|
80
80
|
pivot: TipSample | null;
|
|
81
81
|
/** Approximate rank of the resolved position, 0-indexed from top. */
|
|
82
|
-
rank:
|
|
82
|
+
rank: bigint;
|
|
83
83
|
/** Approximate gas-from-top of the resolved position. */
|
|
84
84
|
gasFromTop: bigint;
|
|
85
85
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-position.d.ts","sourceRoot":"","sources":["../src/block-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,CAAA;AAExC,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAA;CACnB;
|
|
1
|
+
{"version":3,"file":"block-position.d.ts","sourceRoot":"","sources":["../src/block-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,CAAA;AAExC,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAA;CACnB;AAoDD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,SAAS,EAAE,EACpB,OAAO,kBAAkB,KACxB,mBAiEF,CAAA"}
|
package/dist/block-position.js
CHANGED
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
* Equality at the same tip is validator-policy-dependent and not
|
|
21
21
|
* guaranteed, so "outbid by 1 wei" is the honest minimum.
|
|
22
22
|
*/
|
|
23
|
-
const sortByTipDesc = (samples) =>
|
|
23
|
+
const sortByTipDesc = (samples) =>
|
|
24
|
+
// Equal-tip arm folded into the descending side — order between
|
|
25
|
+
// equal-tip samples is unspecified and consumers shouldn't rely on
|
|
26
|
+
// it (mempool ordering is provider-dependent anyway).
|
|
27
|
+
[...samples].sort((a, b) => (a.tip > b.tip ? -1 : 1));
|
|
24
28
|
const matchesIdentifier = (sample, id) => {
|
|
25
29
|
if ('hash' in id) {
|
|
26
30
|
return (typeof sample.hash === 'string' &&
|
|
@@ -35,31 +39,33 @@ const matchesIdentifier = (sample, id) => {
|
|
|
35
39
|
/**
|
|
36
40
|
* Walk samples in tip-desc order accumulating gas; return the index
|
|
37
41
|
* (0-based from top) where cumulative gas first crosses `targetGas`.
|
|
38
|
-
* Returns -
|
|
42
|
+
* Returns -1n when the target exceeds the sum of all gas (the position
|
|
39
43
|
* is below the whole distribution).
|
|
40
44
|
*/
|
|
41
45
|
const indexAtGasOffset = (sorted, targetGas) => {
|
|
42
46
|
if (targetGas <= 0n)
|
|
43
|
-
return
|
|
47
|
+
return 0n;
|
|
44
48
|
let cumulative = 0n;
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const len = BigInt(sorted.length);
|
|
50
|
+
for (let i = 0n; i < len; i += 1n) {
|
|
51
|
+
cumulative += sorted[Number(i)].gas;
|
|
47
52
|
if (cumulative > targetGas)
|
|
48
53
|
return i;
|
|
49
54
|
}
|
|
50
|
-
return -
|
|
55
|
+
return -1n;
|
|
51
56
|
};
|
|
52
57
|
const sumGasUpTo = (sorted, indexExclusive) => {
|
|
53
58
|
let g = 0n;
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const len = BigInt(sorted.length);
|
|
60
|
+
const upper = indexExclusive < len ? indexExclusive : len;
|
|
61
|
+
for (let i = 0n; i < upper; i += 1n)
|
|
62
|
+
g += sorted[Number(i)].gas;
|
|
57
63
|
return g;
|
|
58
64
|
};
|
|
59
65
|
const empty = () => ({
|
|
60
66
|
requiredTip: 0n,
|
|
61
67
|
pivot: null,
|
|
62
|
-
rank:
|
|
68
|
+
rank: 0n,
|
|
63
69
|
gasFromTop: 0n,
|
|
64
70
|
});
|
|
65
71
|
/**
|
|
@@ -76,6 +82,7 @@ export const tipForBlockPosition = (samples, query) => {
|
|
|
76
82
|
if (samples.length === 0)
|
|
77
83
|
return empty();
|
|
78
84
|
const sorted = sortByTipDesc(samples);
|
|
85
|
+
const len = BigInt(sorted.length);
|
|
79
86
|
let pivotIndex;
|
|
80
87
|
let beatPivot; // true = outbid (tip+1), false = undercut (tip-1)
|
|
81
88
|
switch (query.kind) {
|
|
@@ -85,12 +92,16 @@ export const tipForBlockPosition = (samples, query) => {
|
|
|
85
92
|
break;
|
|
86
93
|
}
|
|
87
94
|
case 'percentile': {
|
|
88
|
-
// 0% = top of block (highest tip); 100% = bottom. Clamp to [
|
|
89
|
-
const pct =
|
|
90
|
-
|
|
95
|
+
// 0% = top of block (highest tip); 100% = bottom. Clamp to [0n, 100n].
|
|
96
|
+
const pct = query.percentile < 0n
|
|
97
|
+
? 0n
|
|
98
|
+
: query.percentile > 100n
|
|
99
|
+
? 100n
|
|
100
|
+
: query.percentile;
|
|
101
|
+
pivotIndex = (len * pct) / 100n;
|
|
91
102
|
// Edge: percentile=100 lands at length, which is "below everything"
|
|
92
|
-
if (pivotIndex >=
|
|
93
|
-
pivotIndex =
|
|
103
|
+
if (pivotIndex >= len)
|
|
104
|
+
pivotIndex = len - 1n;
|
|
94
105
|
beatPivot = true;
|
|
95
106
|
break;
|
|
96
107
|
}
|
|
@@ -101,21 +112,22 @@ export const tipForBlockPosition = (samples, query) => {
|
|
|
101
112
|
}
|
|
102
113
|
case 'aheadOf':
|
|
103
114
|
case 'behind': {
|
|
104
|
-
|
|
115
|
+
const found = sorted.findIndex((s) => matchesIdentifier(s, query.tx));
|
|
116
|
+
pivotIndex = found === -1 ? -1n : BigInt(found);
|
|
105
117
|
beatPivot = query.kind === 'aheadOf';
|
|
106
118
|
break;
|
|
107
119
|
}
|
|
108
120
|
}
|
|
109
|
-
if (pivotIndex <
|
|
121
|
+
if (pivotIndex < 0n || pivotIndex >= len) {
|
|
110
122
|
// Position is below everyone (or pivot not found) — pay nothing in priority
|
|
111
123
|
return {
|
|
112
124
|
requiredTip: 0n,
|
|
113
125
|
pivot: null,
|
|
114
|
-
rank:
|
|
115
|
-
gasFromTop: sumGasUpTo(sorted,
|
|
126
|
+
rank: len,
|
|
127
|
+
gasFromTop: sumGasUpTo(sorted, len),
|
|
116
128
|
};
|
|
117
129
|
}
|
|
118
|
-
const pivot = sorted[pivotIndex];
|
|
130
|
+
const pivot = sorted[Number(pivotIndex)];
|
|
119
131
|
const requiredTip = beatPivot
|
|
120
132
|
? pivot.tip + 1n
|
|
121
133
|
: pivot.tip > 0n
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-position.js","sourceRoot":"","sources":["../src/block-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA0DH,MAAM,aAAa,GAAG,CAAC,OAAoB,EAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"block-position.js","sourceRoot":"","sources":["../src/block-position.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inverse of `tipForBlockPosition`. Given a tip and a snapshot, find
|
|
3
|
+
* where the tip would land in the live distribution and which named
|
|
4
|
+
* tier it falls in. Pure: no I/O, no oracle dependency, no wall-clock.
|
|
5
|
+
*
|
|
6
|
+
* Useful for "you priced at the Xth percentile" UI affordances and for
|
|
7
|
+
* post-hoc "why is my tx slow" diagnostics.
|
|
8
|
+
*/
|
|
9
|
+
import { TierName, type GasOracleState } from './types.js';
|
|
10
|
+
export interface ClassifyTipResult {
|
|
11
|
+
/**
|
|
12
|
+
* Named tier the tip falls in. `null` when below `TierName.slow`'s
|
|
13
|
+
* `maxPriorityFeePerGas` floor.
|
|
14
|
+
*/
|
|
15
|
+
tier: TierName | null;
|
|
16
|
+
/**
|
|
17
|
+
* `maxPriorityFeePerGas` floor of the next tier above `tier`. `null`
|
|
18
|
+
* when `tier === TierName.instant` (already at top).
|
|
19
|
+
*/
|
|
20
|
+
requiredForNextTier: bigint | null;
|
|
21
|
+
/**
|
|
22
|
+
* Approximate percentile in the live distribution (block ring tips +
|
|
23
|
+
* mempool samples), 0n..100n. `0n` = top of block (highest tip);
|
|
24
|
+
* `100n` = bottom. `0n` when distribution empty.
|
|
25
|
+
*/
|
|
26
|
+
percentile: bigint;
|
|
27
|
+
/** Approximate rank, 0n-indexed from top. `0n` when distribution empty. */
|
|
28
|
+
rank: bigint;
|
|
29
|
+
/** Accumulated gas above this tip's position. `0n` when empty. */
|
|
30
|
+
gasFromTop: bigint;
|
|
31
|
+
}
|
|
32
|
+
export declare const classifyTip: (snapshot: GasOracleState, tipWei: bigint) => ClassifyTipResult;
|
|
33
|
+
//# sourceMappingURL=classify-tip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-tip.d.ts","sourceRoot":"","sources":["../src/classify-tip.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAEL,QAAQ,EACR,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAA;CACnB;AA4BD,eAAO,MAAM,WAAW,GACtB,UAAU,cAAc,EACxB,QAAQ,MAAM,KACb,iBA0BF,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inverse of `tipForBlockPosition`. Given a tip and a snapshot, find
|
|
3
|
+
* where the tip would land in the live distribution and which named
|
|
4
|
+
* tier it falls in. Pure: no I/O, no oracle dependency, no wall-clock.
|
|
5
|
+
*
|
|
6
|
+
* Useful for "you priced at the Xth percentile" UI affordances and for
|
|
7
|
+
* post-hoc "why is my tx slow" diagnostics.
|
|
8
|
+
*/
|
|
9
|
+
import { TIER_LADDER, } from './types.js';
|
|
10
|
+
const tierForTip = (tiers, tipWei) => {
|
|
11
|
+
for (let i = TIER_LADDER.length - 1; i >= 0; i -= 1) {
|
|
12
|
+
const tier = TIER_LADDER[i];
|
|
13
|
+
if (tipWei >= tiers[tier].maxPriorityFeePerGas)
|
|
14
|
+
return tier;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
const requiredForNextTierAbove = (tiers, currentTier) => {
|
|
19
|
+
const currentIndex = currentTier ? TIER_LADDER.indexOf(currentTier) : -1;
|
|
20
|
+
const nextIndex = currentIndex + 1;
|
|
21
|
+
if (nextIndex >= TIER_LADDER.length)
|
|
22
|
+
return null;
|
|
23
|
+
return tiers[TIER_LADDER[nextIndex]].maxPriorityFeePerGas;
|
|
24
|
+
};
|
|
25
|
+
const collectDistribution = (snapshot) => [
|
|
26
|
+
...snapshot.ring.flatMap((block) => block.tips),
|
|
27
|
+
...snapshot.mempoolSamples,
|
|
28
|
+
];
|
|
29
|
+
export const classifyTip = (snapshot, tipWei) => {
|
|
30
|
+
const tier = tierForTip(snapshot.tiers, tipWei);
|
|
31
|
+
const requiredForNextTier = requiredForNextTierAbove(snapshot.tiers, tier);
|
|
32
|
+
const samples = collectDistribution(snapshot);
|
|
33
|
+
if (samples.length === 0) {
|
|
34
|
+
return { tier, requiredForNextTier, percentile: 0n, rank: 0n, gasFromTop: 0n };
|
|
35
|
+
}
|
|
36
|
+
// Sort by tip desc, equal-tip arm folded into descending side
|
|
37
|
+
// (matches block-position.ts convention).
|
|
38
|
+
const sorted = [...samples].sort((a, b) => (a.tip > b.tip ? -1 : 1));
|
|
39
|
+
const firstWeakerIndexNum = sorted.findIndex((s) => s.tip <= tipWei);
|
|
40
|
+
const samplesLen = BigInt(sorted.length);
|
|
41
|
+
const rank = firstWeakerIndexNum === -1
|
|
42
|
+
? samplesLen
|
|
43
|
+
: BigInt(firstWeakerIndexNum);
|
|
44
|
+
// Round-half-away-from-zero percentile, all bigint. samplesLen >= 1n
|
|
45
|
+
// is guaranteed by the early-return on empty samples above.
|
|
46
|
+
const percentile = (rank * 100n + samplesLen / 2n) / samplesLen;
|
|
47
|
+
let gasFromTop = 0n;
|
|
48
|
+
for (let i = 0n; i < rank; i += 1n)
|
|
49
|
+
gasFromTop += sorted[Number(i)].gas;
|
|
50
|
+
return { tier, requiredForNextTier, percentile, rank, gasFromTop };
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=classify-tip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-tip.js","sourceRoot":"","sources":["../src/classify-tip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default English UI copy mapping each tier to a user-facing inclusion
|
|
3
|
+
* label, plus a small helper that resolves a tier with optional partial
|
|
4
|
+
* overrides (locale or branded copy without forking the whole map).
|
|
5
|
+
*
|
|
6
|
+
* Conservative phrasing — labels describe relative position in the next
|
|
7
|
+
* block, not hard guarantees. Real inclusion is probabilistic.
|
|
8
|
+
*/
|
|
9
|
+
import { TierName } from './types.js';
|
|
10
|
+
export declare const defaultInclusionLabels: Record<TierName, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve a tier to its inclusion label, falling back to
|
|
13
|
+
* `defaultInclusionLabels` for any tier not present in `overrides`.
|
|
14
|
+
*
|
|
15
|
+
* Locale / branded-copy pattern (no fork required):
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const es: Partial<Record<TierName, string>> = {
|
|
19
|
+
* [TierName.standard]: 'Próximo bloque',
|
|
20
|
+
* [TierName.fast]: 'Cabeza del próximo bloque',
|
|
21
|
+
* }
|
|
22
|
+
* inclusionLabel(TierName.standard, es) // 'Próximo bloque'
|
|
23
|
+
* inclusionLabel(TierName.slow, es) // falls back to default English
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Consumers can also fully replace the map by spreading:
|
|
27
|
+
* `const myLabels = { ...defaultInclusionLabels, ...partial }`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const inclusionLabel: (tier: TierName, overrides?: Partial<Record<TierName, string>>) => string;
|
|
30
|
+
//# sourceMappingURL=inclusion-labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inclusion-labels.d.ts","sourceRoot":"","sources":["../src/inclusion-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAK3D,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,QAAQ,EACd,YAAY,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAC5C,MAA2D,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default English UI copy mapping each tier to a user-facing inclusion
|
|
3
|
+
* label, plus a small helper that resolves a tier with optional partial
|
|
4
|
+
* overrides (locale or branded copy without forking the whole map).
|
|
5
|
+
*
|
|
6
|
+
* Conservative phrasing — labels describe relative position in the next
|
|
7
|
+
* block, not hard guarantees. Real inclusion is probabilistic.
|
|
8
|
+
*/
|
|
9
|
+
import { TierName } from './types.js';
|
|
10
|
+
export const defaultInclusionLabels = {
|
|
11
|
+
[TierName.slow]: 'Within a few blocks',
|
|
12
|
+
[TierName.standard]: 'Next block',
|
|
13
|
+
[TierName.fast]: 'Top of next block',
|
|
14
|
+
[TierName.instant]: 'Front of next block',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a tier to its inclusion label, falling back to
|
|
18
|
+
* `defaultInclusionLabels` for any tier not present in `overrides`.
|
|
19
|
+
*
|
|
20
|
+
* Locale / branded-copy pattern (no fork required):
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const es: Partial<Record<TierName, string>> = {
|
|
24
|
+
* [TierName.standard]: 'Próximo bloque',
|
|
25
|
+
* [TierName.fast]: 'Cabeza del próximo bloque',
|
|
26
|
+
* }
|
|
27
|
+
* inclusionLabel(TierName.standard, es) // 'Próximo bloque'
|
|
28
|
+
* inclusionLabel(TierName.slow, es) // falls back to default English
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Consumers can also fully replace the map by spreading:
|
|
32
|
+
* `const myLabels = { ...defaultInclusionLabels, ...partial }`.
|
|
33
|
+
*/
|
|
34
|
+
export const inclusionLabel = (tier, overrides) => overrides?.[tier] ?? defaultInclusionLabels[tier];
|
|
35
|
+
//# sourceMappingURL=inclusion-labels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inclusion-labels.js","sourceRoot":"","sources":["../src/inclusion-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB;IACtC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY;IACjC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB;IACpC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB;CAC1C,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,IAAc,EACd,SAA6C,EACrC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,9 +16,15 @@ export { createGasOracle, reducePollInputs, sampleGasFees, type CreateGasOracleO
|
|
|
16
16
|
export { effectiveTip, computePercentiles, detectTrend, cappedTip, computeTiers, computeBlobBaseFee, flattenTxPool, gasWeightedPercentiles, sortedTips, DEFAULT_PRIORITY_FEE_DECAY_CAP, DEFAULT_BASE_FEE_LIVENESS_BLOCKS, } from './math.js';
|
|
17
17
|
export { blockToSample, mempoolToSamples, } from './samples.js';
|
|
18
18
|
export { fetchOracleInputs, fetchHeadBlockNumber, type FeeHistoryResult, type BlockResult, type TxPoolContent, type OraclePollInputs, } from './transport.js';
|
|
19
|
-
export type { RawTx, TipPercentiles, TierRecommendation,
|
|
19
|
+
export type { RawTx, TipPercentiles, TierRecommendation, MempoolStats, BlobStats, BlockSample, GasOracleState, TipSample, PollOptions, } from './types.js';
|
|
20
|
+
export { PriorityModel, TierName, TIER_LADDER, Trend, TxType } from './types.js';
|
|
20
21
|
export { normalizeMempool, findByHash, findByAddressNonce, findInMempool, } from './mempool.js';
|
|
21
22
|
export type { NormalizedMempool, TxIdentifier, MempoolBucket, MempoolHit, } from './mempool.js';
|
|
22
23
|
export { tipForBlockPosition } from './block-position.js';
|
|
23
24
|
export type { BlockPositionQuery, BlockPositionResult, } from './block-position.js';
|
|
25
|
+
export { minimumReplacementFee, bumpForReplacement, recommendBumpTier, BumpStrategy, ReplacementBumpPercent, } from './replacement.js';
|
|
26
|
+
export type { RecommendBumpTierOptions, ReplacementGas, } from './replacement.js';
|
|
27
|
+
export { classifyTip, type ClassifyTipResult } from './classify-tip.js';
|
|
28
|
+
export { defaultInclusionLabels, inclusionLabel } from './inclusion-labels.js';
|
|
29
|
+
export { chainPresets, presetForChainId, type ChainPreset, } from './presets.js';
|
|
24
30
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,SAAS,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,UAAU,EACV,8BAA8B,EAC9B,gCAAgC,GACjC,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,SAAS,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,UAAU,EACV,8BAA8B,EAC9B,gCAAgC,GACjC,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGhF,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,UAAU,GACX,MAAM,cAAc,CAAA;AAGrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,YAAY,EACV,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,GACvB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,wBAAwB,EACxB,cAAc,GACf,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAGvE,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAG9E,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAA"}
|