@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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same-nonce EIP-1559 replacement helpers. Pure functions; no I/O.
|
|
3
|
+
*
|
|
4
|
+
* The protocol-replacement-floor math is verified against geth
|
|
5
|
+
* `core/txpool/legacypool/list.go:Add()`, geth `core/txpool/blobpool/
|
|
6
|
+
* config.go`, reth `crates/transaction-pool/src/config.rs`, and PulseChain
|
|
7
|
+
* `gitlab.com/pulsechaincom/go-pulse/master/core/txpool/legacypool/
|
|
8
|
+
* legacypool.go`. The `+1n` term in `minimumReplacementFee` is load-
|
|
9
|
+
* bearing for small `current` values where geth's integer-floor threshold
|
|
10
|
+
* collapses below the strict `old < tx` check.
|
|
11
|
+
*/
|
|
12
|
+
import { TIER_LADDER, TierName, TxType } from './types.js';
|
|
13
|
+
import { tipForBlockPosition } from './block-position.js';
|
|
14
|
+
export const ReplacementBumpPercent = {
|
|
15
|
+
/** geth `legacypool.DefaultConfig.PriceBump` — legacy / EIP-2930 / EIP-1559 / EIP-7702. */
|
|
16
|
+
default: 10n,
|
|
17
|
+
/** geth `blobpool.DefaultConfig.PriceBump` — EIP-4844 blob txs. */
|
|
18
|
+
blob: 100n,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Minimum fee required to replace a tx at a given current fee. Per-field
|
|
22
|
+
* primitive — apply once to `maxFeePerGas` and once to
|
|
23
|
+
* `maxPriorityFeePerGas` for an EIP-1559 replacement.
|
|
24
|
+
*
|
|
25
|
+
* Returns `(current * (100 + bump)) / 100 + 1`, where `bump` is `100n`
|
|
26
|
+
* for blob txs and `10n` otherwise. The `+1` clears geth's strict
|
|
27
|
+
* `old < tx` check at small `current` values where the integer-floor
|
|
28
|
+
* threshold collapses (e.g. `current=1`, `floor(1.1)=1`).
|
|
29
|
+
*
|
|
30
|
+
* Unknown future tx-type bytes default to the legacy/1559 +10% bump —
|
|
31
|
+
* the +100% rule is blob-specific.
|
|
32
|
+
*/
|
|
33
|
+
export const minimumReplacementFee = (current, txType) => {
|
|
34
|
+
const bump = txType === TxType.blob
|
|
35
|
+
? ReplacementBumpPercent.blob
|
|
36
|
+
: ReplacementBumpPercent.default;
|
|
37
|
+
return (current * (100n + bump)) / 100n + 1n;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Compute (maxFeePerGas, maxPriorityFeePerGas) for replacing a 1559 tx.
|
|
41
|
+
* Per-field rule: max(target, protocolFloor). Final guard ensures
|
|
42
|
+
* `result.maxFeePerGas >= result.maxPriorityFeePerGas` so the result is
|
|
43
|
+
* a well-formed tx even on degenerate target inputs.
|
|
44
|
+
*
|
|
45
|
+
* 1559-scoped — for blob replacement use `minimumReplacementFee(_,
|
|
46
|
+
* TxType.blob)` directly per fee field.
|
|
47
|
+
*/
|
|
48
|
+
export const bumpForReplacement = (currentGas, targetGas) => {
|
|
49
|
+
const maxFeeFloor = minimumReplacementFee(currentGas.maxFeePerGas, TxType.eip1559);
|
|
50
|
+
const priorityFloor = minimumReplacementFee(currentGas.maxPriorityFeePerGas, TxType.eip1559);
|
|
51
|
+
const maxPriorityFeePerGas = targetGas.maxPriorityFeePerGas > priorityFloor
|
|
52
|
+
? targetGas.maxPriorityFeePerGas
|
|
53
|
+
: priorityFloor;
|
|
54
|
+
let maxFeePerGas = targetGas.maxFeePerGas > maxFeeFloor ? targetGas.maxFeePerGas : maxFeeFloor;
|
|
55
|
+
if (maxFeePerGas < maxPriorityFeePerGas)
|
|
56
|
+
maxFeePerGas = maxPriorityFeePerGas;
|
|
57
|
+
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
58
|
+
};
|
|
59
|
+
export const BumpStrategy = {
|
|
60
|
+
cheapestThatLands: 'cheapestThatLands',
|
|
61
|
+
oneStepFasterThanRecommended: 'oneStepFasterThanRecommended',
|
|
62
|
+
instant: 'instant',
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Pick a tier to bump to for a same-nonce EIP-1559 replacement. The
|
|
66
|
+
* effective floor is `max(protocolFloor, outpaceFloor)`:
|
|
67
|
+
* - protocolFloor = `minimumReplacementFee(stuckTx.priorityTip,
|
|
68
|
+
* TxType.eip1559)` — the geth +10% rule.
|
|
69
|
+
* - outpaceFloor = the tip required to be ranked strictly above every
|
|
70
|
+
* tx currently ahead of `stuckTx.identifier` in
|
|
71
|
+
* `snapshot.mempoolSamples`. Computed by locating stuck's rank via
|
|
72
|
+
* `tipForBlockPosition({ kind: 'aheadOf', tx: identifier })` then
|
|
73
|
+
* querying `{ kind: 'rank', rank: stuckRank - 1n }` to outbid the
|
|
74
|
+
* tx currently just above stuck. When stuck is already at the top
|
|
75
|
+
* of the distribution (or not present), this is `0n` and only the
|
|
76
|
+
* protocol floor applies.
|
|
77
|
+
*
|
|
78
|
+
* Returns `null` when no tier clears the effective floor — the original
|
|
79
|
+
* was already paying above the top of the ladder, or the snapshot has
|
|
80
|
+
* no tip data.
|
|
81
|
+
*/
|
|
82
|
+
export const recommendBumpTier = (snapshot, stuckTx, options = {}) => {
|
|
83
|
+
const strategy = options.strategy ?? BumpStrategy.cheapestThatLands;
|
|
84
|
+
const protocolFloor = minimumReplacementFee(stuckTx.priorityTip, TxType.eip1559);
|
|
85
|
+
const outpaceFloor = stuckTx.identifier
|
|
86
|
+
? (() => {
|
|
87
|
+
const stuckPosition = tipForBlockPosition(snapshot.mempoolSamples, {
|
|
88
|
+
kind: 'aheadOf',
|
|
89
|
+
tx: stuckTx.identifier,
|
|
90
|
+
});
|
|
91
|
+
// Stuck not in mempool, or stuck already at rank 0 (no one above).
|
|
92
|
+
if (stuckPosition.pivot === null || stuckPosition.rank === 0n)
|
|
93
|
+
return 0n;
|
|
94
|
+
// Outbid the tx at rank `stuckRank - 1` (the cheapest tx
|
|
95
|
+
// currently ahead of stuck) — landing here is sufficient to
|
|
96
|
+
// outpace every tx currently above stuck.
|
|
97
|
+
return tipForBlockPosition(snapshot.mempoolSamples, {
|
|
98
|
+
kind: 'rank',
|
|
99
|
+
rank: stuckPosition.rank - 1n,
|
|
100
|
+
}).requiredTip;
|
|
101
|
+
})()
|
|
102
|
+
: 0n;
|
|
103
|
+
const floor = protocolFloor > outpaceFloor ? protocolFloor : outpaceFloor;
|
|
104
|
+
const cheapestIndex = TIER_LADDER.findIndex((tier) => snapshot.tiers[tier].maxPriorityFeePerGas > floor);
|
|
105
|
+
if (cheapestIndex === -1)
|
|
106
|
+
return null;
|
|
107
|
+
if (strategy === BumpStrategy.cheapestThatLands)
|
|
108
|
+
return TIER_LADDER[cheapestIndex];
|
|
109
|
+
if (strategy === BumpStrategy.instant)
|
|
110
|
+
return TierName.instant;
|
|
111
|
+
// oneStepFasterThanRecommended
|
|
112
|
+
return TIER_LADDER[Math.min(cheapestIndex + 1, TIER_LADDER.length - 1)];
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=replacement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replacement.js","sourceRoot":"","sources":["../src/replacement.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -46,11 +46,29 @@ export interface TierRecommendation {
|
|
|
46
46
|
gasPrice: bigint;
|
|
47
47
|
maxFeePerBlobGas: bigint | null;
|
|
48
48
|
}
|
|
49
|
-
export
|
|
50
|
-
|
|
49
|
+
export declare const Trend: {
|
|
50
|
+
readonly rising: "rising";
|
|
51
|
+
readonly falling: "falling";
|
|
52
|
+
readonly stable: "stable";
|
|
53
|
+
};
|
|
54
|
+
export type Trend = (typeof Trend)[keyof typeof Trend];
|
|
55
|
+
export declare const TierName: {
|
|
56
|
+
readonly slow: "slow";
|
|
57
|
+
readonly standard: "standard";
|
|
58
|
+
readonly fast: "fast";
|
|
59
|
+
readonly instant: "instant";
|
|
60
|
+
};
|
|
61
|
+
export type TierName = (typeof TierName)[keyof typeof TierName];
|
|
62
|
+
/**
|
|
63
|
+
* Canonical tier ordering, slow → instant. Used by helpers that walk the
|
|
64
|
+
* tier ladder (e.g., `classifyTip` finds the highest tier whose floor a
|
|
65
|
+
* tip clears; `recommendBumpTier` finds the cheapest tier that clears
|
|
66
|
+
* the protocol floor + outpace floor). Ordering is load-bearing.
|
|
67
|
+
*/
|
|
68
|
+
export declare const TIER_LADDER: readonly TierName[];
|
|
51
69
|
export interface MempoolStats {
|
|
52
|
-
pendingCount:
|
|
53
|
-
queuedCount:
|
|
70
|
+
pendingCount: bigint;
|
|
71
|
+
queuedCount: bigint;
|
|
54
72
|
/** Sum of `tx.gas` across all pending txs — congestion proxy. */
|
|
55
73
|
pendingGasDemand: bigint;
|
|
56
74
|
/** Latest block's gas limit, useful for "pending demand vs. block capacity". */
|
|
@@ -104,7 +122,24 @@ export interface TipSample {
|
|
|
104
122
|
* Future cutoffs can be added (e.g. `'eip4844'` for blob-only priority)
|
|
105
123
|
* without re-interpreting existing values.
|
|
106
124
|
*/
|
|
107
|
-
export
|
|
125
|
+
export declare const PriorityModel: {
|
|
126
|
+
readonly flat: "flat";
|
|
127
|
+
readonly eip1559: "eip1559";
|
|
128
|
+
};
|
|
129
|
+
export type PriorityModel = (typeof PriorityModel)[keyof typeof PriorityModel];
|
|
130
|
+
/**
|
|
131
|
+
* EIP-2718 transaction type bytes. Identifier values — never participate
|
|
132
|
+
* in arithmetic, so they stay `number` per the package-wide bigint
|
|
133
|
+
* carve-out.
|
|
134
|
+
*/
|
|
135
|
+
export declare const TxType: {
|
|
136
|
+
readonly legacy: 0;
|
|
137
|
+
readonly eip2930: 1;
|
|
138
|
+
readonly eip1559: 2;
|
|
139
|
+
readonly blob: 3;
|
|
140
|
+
readonly setCodeAuthorization: 4;
|
|
141
|
+
};
|
|
142
|
+
export type TxType = (typeof TxType)[keyof typeof TxType];
|
|
108
143
|
/**
|
|
109
144
|
* Producer-side toggles: which RPCs the oracle calls upstream each cycle.
|
|
110
145
|
*
|
|
@@ -162,6 +197,15 @@ export interface GasOracleState {
|
|
|
162
197
|
* §7-§9.
|
|
163
198
|
*/
|
|
164
199
|
ring: BlockSample[];
|
|
200
|
+
/**
|
|
201
|
+
* Live mempool samples used to compute this snapshot's tiers.
|
|
202
|
+
* Producer-local — wire publishers should strip before serializing
|
|
203
|
+
* (same convention as `ring`). Consumed by replacement / classification
|
|
204
|
+
* helpers (e.g., `recommendBumpTier`'s outpace correction) for live-
|
|
205
|
+
* distribution analysis without re-fetching mempool data. Each poll
|
|
206
|
+
* replaces this field; no cumulative growth.
|
|
207
|
+
*/
|
|
208
|
+
mempoolSamples: TipSample[];
|
|
165
209
|
lastPublishedTips?: Record<TierName, bigint>;
|
|
166
210
|
lastPublishedBlockNumber?: bigint;
|
|
167
211
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,KAAK;IACpB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,oBAAoB,EAAE,MAAM,CAAA;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAED,MAAM,MAAM,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -7,5 +7,61 @@
|
|
|
7
7
|
* bigint values. The caller owns that encoding — this package keeps the
|
|
8
8
|
* canonical numeric form internally.
|
|
9
9
|
*/
|
|
10
|
-
export {
|
|
10
|
+
export const Trend = {
|
|
11
|
+
rising: 'rising',
|
|
12
|
+
falling: 'falling',
|
|
13
|
+
stable: 'stable',
|
|
14
|
+
};
|
|
15
|
+
export const TierName = {
|
|
16
|
+
slow: 'slow',
|
|
17
|
+
standard: 'standard',
|
|
18
|
+
fast: 'fast',
|
|
19
|
+
instant: 'instant',
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Canonical tier ordering, slow → instant. Used by helpers that walk the
|
|
23
|
+
* tier ladder (e.g., `classifyTip` finds the highest tier whose floor a
|
|
24
|
+
* tip clears; `recommendBumpTier` finds the cheapest tier that clears
|
|
25
|
+
* the protocol floor + outpace floor). Ordering is load-bearing.
|
|
26
|
+
*/
|
|
27
|
+
export const TIER_LADDER = [
|
|
28
|
+
TierName.slow,
|
|
29
|
+
TierName.standard,
|
|
30
|
+
TierName.fast,
|
|
31
|
+
TierName.instant,
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Where the chain's inclusion logic draws its priority cutoff in the
|
|
35
|
+
* tx-type space.
|
|
36
|
+
*
|
|
37
|
+
* - `'flat'` — chain ignores the EIP-2718 type byte for ordering.
|
|
38
|
+
* Tiers derive from a single gas-weighted distribution
|
|
39
|
+
* across all txs. Right for extractive validators
|
|
40
|
+
* (PulseChain et al.) where the only signal that matters
|
|
41
|
+
* is fee per gas, regardless of tx envelope.
|
|
42
|
+
* - `'eip1559'` — type 2+ txs get priority. Paying-lane tiers
|
|
43
|
+
* (standard/fast/instant) derive from type-2+ samples
|
|
44
|
+
* only; `slow` still draws from the full distribution
|
|
45
|
+
* so legacy senders find their lane. Right for chains
|
|
46
|
+
* that honor the 1559 fee-market shape.
|
|
47
|
+
*
|
|
48
|
+
* Future cutoffs can be added (e.g. `'eip4844'` for blob-only priority)
|
|
49
|
+
* without re-interpreting existing values.
|
|
50
|
+
*/
|
|
51
|
+
export const PriorityModel = {
|
|
52
|
+
flat: 'flat',
|
|
53
|
+
eip1559: 'eip1559',
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* EIP-2718 transaction type bytes. Identifier values — never participate
|
|
57
|
+
* in arithmetic, so they stay `number` per the package-wide bigint
|
|
58
|
+
* carve-out.
|
|
59
|
+
*/
|
|
60
|
+
export const TxType = {
|
|
61
|
+
legacy: 0,
|
|
62
|
+
eip2930: 1,
|
|
63
|
+
eip1559: 2,
|
|
64
|
+
blob: 3,
|
|
65
|
+
setCodeAuthorization: 4,
|
|
66
|
+
};
|
|
11
67
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4CH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;CACR,CAAA;AAGV,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;CACV,CAAA;AAGV;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAwB;IAC9C,QAAQ,CAAC,IAAI;IACb,QAAQ,CAAC,QAAQ;IACjB,QAAQ,CAAC,IAAI;IACb,QAAQ,CAAC,OAAO;CACR,CAAA;AA2CV;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;CACV,CAAA;AAGV;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,CAAC;IACP,oBAAoB,EAAE,CAAC;CACf,CAAA"}
|
package/dist/viem-transport.d.ts
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { type Transport } from 'viem';
|
|
34
34
|
import { type CreateGasOracleOptions } from './oracle.js';
|
|
35
|
-
import
|
|
35
|
+
import { TierName } from './types.js';
|
|
36
36
|
export interface InterceptOptions {
|
|
37
37
|
/**
|
|
38
38
|
* Additive multi-tier read. Returns `{ baseFee, tiers: { slow, standard,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viem-transport.d.ts","sourceRoot":"","sources":["../src/viem-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,MAAM,CAAA;AAE7C,OAAO,EAAmB,KAAK,sBAAsB,EAAkB,MAAM,aAAa,CAAA;AAC1F,OAAO,
|
|
1
|
+
{"version":3,"file":"viem-transport.d.ts","sourceRoot":"","sources":["../src/viem-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,MAAM,CAAA;AAE7C,OAAO,EAAmB,KAAK,sBAAsB,EAAkB,MAAM,aAAa,CAAA;AAC1F,OAAO,EAAE,QAAQ,EAAwD,MAAM,YAAY,CAAA;AAE3F,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;IAC/B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAC5C;AAED,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC;IAClF;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAC7B;AAED,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG;IAAE,aAAa,EAAE,MAAM,IAAI,CAAA;CAAE,CAAA;AAmH1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GACxB,gBAAgB,SAAS,EACzB,SAAS,oBAAoB,KAC5B,kBAyCF,CAAA"}
|
package/dist/viem-transport.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { custom } from 'viem';
|
|
34
34
|
import { createGasOracle } from './oracle.js';
|
|
35
|
+
import { TierName, TxType } from './types.js';
|
|
35
36
|
/**
|
|
36
37
|
* Sentinel returned by `dispatchIntercept` when no intercept matched
|
|
37
38
|
* and the request should be passed through to the inner transport.
|
|
@@ -40,23 +41,28 @@ import { createGasOracle } from './oracle.js';
|
|
|
40
41
|
*/
|
|
41
42
|
const PASSTHROUGH = Symbol('gas-oracle:passthrough');
|
|
42
43
|
const toHex = (n) => '0x' + n.toString(16);
|
|
43
|
-
const TIER_NAMES = [
|
|
44
|
+
const TIER_NAMES = [
|
|
45
|
+
TierName.instant,
|
|
46
|
+
TierName.fast,
|
|
47
|
+
TierName.standard,
|
|
48
|
+
TierName.slow,
|
|
49
|
+
];
|
|
44
50
|
/**
|
|
45
51
|
* Format a single tier for the `eth_gasFeeEstimate` response, scoped
|
|
46
52
|
* to the requested tx type. Mirrors the relay's `gas-intercept.ts`
|
|
47
53
|
* formatter so consumers of either surface get identical wire shapes.
|
|
48
54
|
*/
|
|
49
55
|
const formatTier = (tier, txType) => {
|
|
50
|
-
if (txType ===
|
|
56
|
+
if (txType === TxType.legacy || txType === TxType.eip2930) {
|
|
51
57
|
return { gasPrice: toHex(tier.gasPrice) };
|
|
52
58
|
}
|
|
53
|
-
if (txType ===
|
|
59
|
+
if (txType === TxType.eip1559 || txType === TxType.setCodeAuthorization) {
|
|
54
60
|
return {
|
|
55
61
|
maxFeePerGas: toHex(tier.maxFeePerGas),
|
|
56
62
|
maxPriorityFeePerGas: toHex(tier.maxPriorityFeePerGas),
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
|
-
if (txType ===
|
|
65
|
+
if (txType === TxType.blob) {
|
|
60
66
|
return {
|
|
61
67
|
maxFeePerGas: toHex(tier.maxFeePerGas),
|
|
62
68
|
maxPriorityFeePerGas: toHex(tier.maxPriorityFeePerGas),
|
|
@@ -85,7 +91,7 @@ const buildGasFeeEstimate = (state, params) => {
|
|
|
85
91
|
baseFeeTrend: state.baseFeeTrend,
|
|
86
92
|
blockNumber: toHex(state.blockNumber),
|
|
87
93
|
lastUpdated: toHex(state.timestamp),
|
|
88
|
-
mempoolPendingCount: state.mempool.pendingCount,
|
|
94
|
+
mempoolPendingCount: Number(state.mempool.pendingCount),
|
|
89
95
|
tiers,
|
|
90
96
|
};
|
|
91
97
|
if (state.blob)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viem-transport.js","sourceRoot":"","sources":["../src/viem-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAA;AAE7C,OAAO,EAAE,eAAe,EAA+C,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"viem-transport.js","sourceRoot":"","sources":["../src/viem-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,MAAM,EAAkB,MAAM,MAAM,CAAA;AAE7C,OAAO,EAAE,eAAe,EAA+C,MAAM,aAAa,CAAA;AAC1F,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAgD,MAAM,YAAY,CAAA;AAwC3F;;;;;GAKG;AACH,MAAM,WAAW,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAA;AAEpD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AAE1D,MAAM,UAAU,GAAe;IAC7B,QAAQ,CAAC,OAAO;IAChB,QAAQ,CAAC,IAAI;IACb,QAAQ,CAAC,QAAQ;IACjB,QAAQ,CAAC,IAAI;CACd,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,GAAG,CACjB,IAAwB,EACxB,MAA0B,EACF,EAAE;IAC1B,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QAC1D,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAA;IAC3C,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,CAAC,OAAO,IAAI,MAAM,KAAK,MAAM,CAAC,oBAAoB,EAAE,CAAC;QACxE,OAAO;YACL,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;YACtC,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC;SACvD,CAAA;IACH,CAAC;IACD,IAAI,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3B,OAAO;YACL,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;YACtC,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC;YACtD,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,CAAC;SACrD,CAAA;IACH,CAAC;IACD,yCAAyC;IACzC,MAAM,GAAG,GAA2B;QAClC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9B,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;QACtC,oBAAoB,EAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC;KACvD,CAAA;IACD,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IACrD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED,MAAM,mBAAmB,GAAG,CAC1B,KAAqB,EACrB,MAAiB,EACQ,EAAE;IAC3B,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACpE,MAAM,KAAK,GAA2C,EAAE,CAAA;IACxD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;IACrD,CAAC;IACD,MAAM,GAAG,GAA4B;QACnC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;QAC7B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;QACrC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;QACnC,mBAAmB,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;QACvD,KAAK;KACN,CAAA;IACD,IAAI,KAAK,CAAC,IAAI;QAAE,GAAG,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC/D,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAC7B,IAA4C,EAC5C,MAAiB,EACjB,SAA2B,EACY,EAAE;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAA;IAEhC,IAAI,IAAI,CAAC,MAAM,KAAK,oBAAoB,IAAI,SAAS,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;QACnF,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,KAAK;YAAE,OAAO,WAAW,CAAA;QAC9B,OAAO,mBAAmB,CAAC,KAAK,EAAE,MAAmB,CAAC,CAAA;IACxD,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,CAAA;QACnC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,WAAW,CAAA;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,KAAK;YAAE,OAAO,WAAW,CAAA;QAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC1C,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,0BAA0B,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,SAAS,CAAC,wBAAwB,CAAA;QAC/C,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,WAAW,CAAA;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5D,IAAI,CAAC,KAAK;YAAE,OAAO,WAAW,CAAA;QAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAA;IACtD,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,cAAyB,EACzB,OAA6B,EACT,EAAE;IACtB,MAAM,SAAS,GAAqB;QAClC,kBAAkB,EAAE,IAAI;QACxB,GAAG,OAAO,CAAC,SAAS;KACrB,CAAA;IAED,wEAAwE;IACxE,oEAAoE;IACpE,mDAAmD;IACnD,MAAM,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC,CAAA;IACtC,+DAA+D;IAC/D,iEAAiE;IACjE,kEAAkE;IAClE,gEAAgE;IAChE,8DAA8D;IAC9D,MAAM,YAAY,GAAG;QACnB,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,SAAS,EAAE,WAAW;KACuC,CAAA;IAC/D,+DAA+D;IAC/D,kEAAkE;IAClE,mEAAmE;IACnE,kEAAkE;IAClE,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,aAAa,EAAE,KAAK;QACpB,GAAG,OAAO;QACV,MAAM,EAAE,YAAY;KACrB,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,OAAO;QAAE,MAAM,CAAC,KAAK,EAAE,CAAA;IAE9D,MAAM,OAAO,GAAG,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,EAAE,IAA4C,EAAE,EAAE;YAC9D,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;YAC/D,IAAI,MAAM,KAAK,WAAW;gBAAE,OAAO,MAAM,CAAA;YACzC,+DAA+D;YAC/D,gEAAgE;YAChE,OAAO,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAW,CAAC,CAAA;QACnF,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AACvE,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valve-tech/gas-oracle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Multi-tier gas-fee oracle for EVM chains. Computes slow/standard/fast/instant tier recommendations from block-included tips, mempool pending tips, and base-fee trend, with a configurable downside-decay cap and a chain-aware EIP-1559 priority cutoff. viem-native — pass it a PublicClient and it does the rest. Ships viem-actions and viem-transport subpaths for drop-in client extension and transport-wrapping.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/valve-tech/evm-toolkit/tree/main/packages/gas-oracle#readme",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"prepare": "yarn build"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@valve-tech/chain-source": "^0.
|
|
62
|
+
"@valve-tech/chain-source": "^0.8.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"viem": "^2.0.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gas-oracle-integration
|
|
3
|
-
description: Integrate `@valve-tech/gas-oracle` into an EVM dapp or backend. Use when the user wants gas-tier recommendations (`slow` / `standard` / `fast` / `instant`), needs to set `maxPriorityFeePerGas` and `maxFeePerGas` for a transaction,
|
|
3
|
+
description: Integrate `@valve-tech/gas-oracle` into an EVM dapp or backend. Use when the user wants gas-tier recommendations (`slow` / `standard` / `fast` / `instant`), needs to set `maxPriorityFeePerGas` and `maxFeePerGas` for a transaction, or asks "how do I price a transaction" against a viem `PublicClient`. Also use when seeing imports from `@valve-tech/gas-oracle` and the user asks for help configuring it per chain (Ethereum, Base, Arbitrum, OP, PulseChain), or asks about `priorityFeeDecayCap`, `priorityModel`, `tipForBlockPosition`, viem-actions, or viem-transport. Also fires when the user asks about composing the oracle with `@valve-tech/tx-tracker` over a shared `ChainSource` — but actual per-tx tracking work belongs in the tx-tracker skill, not here.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Integrating `@valve-tech/gas-oracle`
|
|
@@ -10,6 +10,8 @@ working in a project that imports the package — it grounds you in the
|
|
|
10
10
|
right configuration choices for the user's chain and the right
|
|
11
11
|
integration shape for their codebase.
|
|
12
12
|
|
|
13
|
+
> **v0.8.0 default change**: `priorityModel` now defaults to `PriorityModel.eip1559` (was `flat`). Examples that previously omitted the field silently get the new default. Set `PriorityModel.flat` explicitly for PulseChain (chain 369) — or use `...chainPresets.pulsechain`.
|
|
14
|
+
|
|
13
15
|
## Decision tree: which integration to use
|
|
14
16
|
|
|
15
17
|
```
|
|
@@ -30,13 +32,13 @@ Is the user already passing a viem PublicClient around?
|
|
|
30
32
|
|
|
31
33
|
| Chain | `chainId` | `priorityModel` | `baseFeeLivenessBlocks` | Notes |
|
|
32
34
|
|---|---|---|---|---|
|
|
33
|
-
| Ethereum mainnet | 1 | `
|
|
34
|
-
| Base | 8453 | `
|
|
35
|
-
| Arbitrum One | 42161 | `
|
|
36
|
-
| Optimism | 10 | `
|
|
37
|
-
| PulseChain mainnet | 369 | `
|
|
38
|
-
| PulseChain testnet v4 | 943 | `
|
|
39
|
-
| Unknown / unsure | — | `
|
|
35
|
+
| Ethereum mainnet | 1 | `PriorityModel.eip1559` (default) | 6 | Validators burn base fee. |
|
|
36
|
+
| Base | 8453 | `PriorityModel.eip1559` (default) | 6 | Same as ETH. |
|
|
37
|
+
| Arbitrum One | 42161 | `PriorityModel.eip1559` (default) | 6 | |
|
|
38
|
+
| Optimism | 10 | `PriorityModel.eip1559` (default) | 6 | |
|
|
39
|
+
| PulseChain mainnet | 369 | `PriorityModel.flat` | 6 | Validators charge tips. Use `...chainPresets.pulsechain`. |
|
|
40
|
+
| PulseChain testnet v4 | 943 | `PriorityModel.flat` | 6 | |
|
|
41
|
+
| Unknown / unsure | — | `PriorityModel.eip1559` (default) | 6 | Default is correct unless you've verified the chain is extractive. |
|
|
40
42
|
|
|
41
43
|
`priorityFeeDecayCap`: leave at default (`WAD/8` = 12.5%/block, EIP-1559
|
|
42
44
|
parity) unless you have a specific reason to tighten/loosen.
|
|
@@ -59,11 +61,12 @@ When reviewing user code, watch for these and suggest fixes:
|
|
|
59
61
|
be missing. Fix: `await oracle.pollOnce()` after `start()` to seed
|
|
60
62
|
state synchronously, then it's safe to call `getState()`.
|
|
61
63
|
|
|
62
|
-
4. **Using `
|
|
63
|
-
chains.**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
4. **Using `PriorityModel.eip1559` on PulseChain or other tip-charging
|
|
65
|
+
chains.** This is now the default — silent footgun on chain 369
|
|
66
|
+
unless you set `PriorityModel.flat` (or use `...chainPresets.pulsechain`).
|
|
67
|
+
It cuts the distribution to type-2+ samples only, but PulseChain
|
|
68
|
+
validators don't honor the type byte — they sort by tip regardless.
|
|
69
|
+
Result: under-published tier values, your tx loses to legacy spam.
|
|
67
70
|
|
|
68
71
|
5. **`keepMempoolSnapshot: true` on a chain whose RPC gates
|
|
69
72
|
`txpool_content`** (most public RPCs). Wastes a poll cycle's RPC
|
|
@@ -89,6 +92,96 @@ import { withGasOracle } from '@valve-tech/gas-oracle/viem-transport'
|
|
|
89
92
|
|
|
90
93
|
`package.json` will show `"@valve-tech/gas-oracle": "^0.2.x"` in dependencies.
|
|
91
94
|
|
|
95
|
+
## Replacement workflow — bumping a stuck tx
|
|
96
|
+
|
|
97
|
+
When the user has a stuck tx and asks how to bump / speed it up,
|
|
98
|
+
recommend the `recommendBumpTier` + `bumpForReplacement` pair. This is
|
|
99
|
+
the canonical caller pattern; don't roll your own +10% math:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import {
|
|
103
|
+
recommendBumpTier,
|
|
104
|
+
bumpForReplacement,
|
|
105
|
+
BumpStrategy,
|
|
106
|
+
} from '@valve-tech/gas-oracle'
|
|
107
|
+
|
|
108
|
+
const tier = recommendBumpTier(
|
|
109
|
+
state,
|
|
110
|
+
{ priorityTip: stuck.maxPriorityFeePerGas, identifier: { hash: stuck.hash } },
|
|
111
|
+
{ strategy: BumpStrategy.cheapestThatLands }, // default
|
|
112
|
+
)
|
|
113
|
+
if (tier === null) return // already paying above top tier — caller's call
|
|
114
|
+
|
|
115
|
+
const target = state.tiers[tier]
|
|
116
|
+
const gas = bumpForReplacement(
|
|
117
|
+
{ maxFeePerGas: stuck.maxFeePerGas, maxPriorityFeePerGas: stuck.maxPriorityFeePerGas },
|
|
118
|
+
{ maxFeePerGas: target.maxFeePerGas, maxPriorityFeePerGas: target.maxPriorityFeePerGas },
|
|
119
|
+
)
|
|
120
|
+
walletClient.sendTransaction({ ...stuck, ...gas })
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`recommendBumpTier` reads `state.mempoolSamples` to compute outpace
|
|
124
|
+
correction (when an `identifier` is supplied) on top of the EIP-1559
|
|
125
|
+
+10% protocol floor. `bumpForReplacement` returns a gas object that
|
|
126
|
+
satisfies BOTH the protocol floor and the target tier — never one or
|
|
127
|
+
the other.
|
|
128
|
+
|
|
129
|
+
## Tip classification
|
|
130
|
+
|
|
131
|
+
Inverse of `tipForBlockPosition`. Given a tip, ask "where would this
|
|
132
|
+
land?" instead of "what tip do I need to land here?":
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { classifyTip } from '@valve-tech/gas-oracle'
|
|
136
|
+
|
|
137
|
+
const result = classifyTip(state, myTip)
|
|
138
|
+
// result.tier — TierName | null (null if below slow)
|
|
139
|
+
// result.requiredForNextTier — bigint floor of next tier above (null at instant)
|
|
140
|
+
// result.percentile — bigint 0-100 (0 = top, 100 = bottom)
|
|
141
|
+
// result.rank — bigint 0-indexed from top
|
|
142
|
+
// result.gasFromTop — bigint accumulated gas above this tip
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Useful for "your fee is low — bump?" UX nudges and for showing a
|
|
146
|
+
user where their existing in-flight tx sits vs. live competition.
|
|
147
|
+
|
|
148
|
+
## UI labels
|
|
149
|
+
|
|
150
|
+
Branded / localized inclusion-time copy without forking the package:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
import { defaultInclusionLabels, inclusionLabel, TierName } from '@valve-tech/gas-oracle'
|
|
154
|
+
|
|
155
|
+
defaultInclusionLabels[TierName.standard] // 'Next block'
|
|
156
|
+
|
|
157
|
+
const es = { [TierName.standard]: 'Próximo bloque' }
|
|
158
|
+
inclusionLabel(TierName.standard, es) // 'Próximo bloque'
|
|
159
|
+
inclusionLabel(TierName.slow, es) // falls back to default English
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Pass partial overrides — anything not in the override map falls back
|
|
163
|
+
to the package default.
|
|
164
|
+
|
|
165
|
+
## Chain presets
|
|
166
|
+
|
|
167
|
+
For PulseChain (and any future entries we ship), use the preset entry
|
|
168
|
+
points instead of typing `chainId` + `priorityModel` by hand:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { createGasOracle, chainPresets, presetForChainId } from '@valve-tech/gas-oracle'
|
|
172
|
+
|
|
173
|
+
// Static — caller knows which chain at code-time:
|
|
174
|
+
createGasOracle({ client, ...chainPresets.pulsechain })
|
|
175
|
+
|
|
176
|
+
// Dynamic — caller has chainId at runtime (e.g. from wallet):
|
|
177
|
+
const preset = presetForChainId(chainId)
|
|
178
|
+
createGasOracle({ client, chainId, ...preset })
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`presetForChainId` returns `undefined` for unknown chains; spreading
|
|
182
|
+
`undefined` into the options object is a no-op, so the call still works
|
|
183
|
+
on a chain we haven't preset (it just gets the package defaults).
|
|
184
|
+
|
|
92
185
|
## Where to find more
|
|
93
186
|
|
|
94
187
|
- Full API + types: `node_modules/@valve-tech/gas-oracle/AGENTS.md`
|
|
@@ -96,3 +189,56 @@ import { withGasOracle } from '@valve-tech/gas-oracle/viem-transport'
|
|
|
96
189
|
- Human-facing docs: `node_modules/@valve-tech/gas-oracle/README.md`
|
|
97
190
|
- Source (when types alone aren't enough): `node_modules/@valve-tech/gas-oracle/dist/`
|
|
98
191
|
(compiled JS + .d.ts) — sources aren't shipped, only built output.
|
|
192
|
+
|
|
193
|
+
## Tx tracking — composing with `@valve-tech/tx-tracker`
|
|
194
|
+
|
|
195
|
+
When the user asks to "track this transaction," "watch tx hash,"
|
|
196
|
+
"know when my tx confirms," or "detect stuck transactions," redirect
|
|
197
|
+
them to `@valve-tech/tx-tracker` (sibling package). The gas oracle
|
|
198
|
+
does NOT track per-tx state — that's a separate concern with its own
|
|
199
|
+
state machine, retention policy, and consumption shapes. See
|
|
200
|
+
`node_modules/@valve-tech/tx-tracker/skills/tx-tracker-integration/SKILL.md`
|
|
201
|
+
for the full integration recipe.
|
|
202
|
+
|
|
203
|
+
The two packages are designed to share one upstream RPC stream:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { createChainSource } from '@valve-tech/chain-source'
|
|
207
|
+
import { createGasOracle } from '@valve-tech/gas-oracle'
|
|
208
|
+
import { createTxTracker } from '@valve-tech/tx-tracker'
|
|
209
|
+
|
|
210
|
+
const source = createChainSource({ client })
|
|
211
|
+
const oracle = createGasOracle({ source, chainId: 1 })
|
|
212
|
+
const tracker = createTxTracker({ source, chainId: 1 })
|
|
213
|
+
|
|
214
|
+
source.start(); oracle.start(); tracker.start()
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`ChainSource` owns the upstream poll cycle. The oracle reads it for
|
|
218
|
+
tier reduction; the tracker reads it for per-tx observations. **One
|
|
219
|
+
upstream RPC poll cycle, two derived views** (per spec §3.1). Each
|
|
220
|
+
surface owns its own lifecycle — `oracle.stop()` does not stop the
|
|
221
|
+
source or the tracker.
|
|
222
|
+
|
|
223
|
+
### Anti-patterns when both are present
|
|
224
|
+
|
|
225
|
+
7. **Constructing a private `ChainSource` for the tracker AND passing
|
|
226
|
+
`client` (not `source`) to `createGasOracle`.** That gives you two
|
|
227
|
+
independent sources for the same chain — double the RPC traffic
|
|
228
|
+
for no functional benefit. Either pass `source` to BOTH, or use
|
|
229
|
+
the `client` shorthand on BOTH (one private source on the oracle's
|
|
230
|
+
side, no tracker — meaningless if you want both).
|
|
231
|
+
|
|
232
|
+
8. **Asking the gas oracle "is my tx confirmed?"** Wrong layer. The
|
|
233
|
+
oracle publishes tier recommendations; it does not observe per-tx
|
|
234
|
+
state. Use `tracker.getTxStatus(hash)` or
|
|
235
|
+
`tracker.subscribe(hash, cb)` from `@valve-tech/tx-tracker`.
|
|
236
|
+
|
|
237
|
+
### Recognizing both packages in the user's code
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { createGasOracle } from '@valve-tech/gas-oracle'
|
|
241
|
+
import { createTxTracker } from '@valve-tech/tx-tracker'
|
|
242
|
+
// `package.json` will have both under dependencies, plus
|
|
243
|
+
// `@valve-tech/chain-source` (the shared foundation both consume).
|
|
244
|
+
```
|