@pythnetwork/price-pusher 10.3.1 → 11.3.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.
Files changed (58) hide show
  1. package/README.md +28 -7
  2. package/dist/aptos/aptos.cjs +7 -7
  3. package/dist/aptos/aptos.d.ts +1 -1
  4. package/dist/aptos/balance-tracker.cjs +7 -7
  5. package/dist/aptos/balance-tracker.d.ts +1 -1
  6. package/dist/aptos/command.cjs +20 -17
  7. package/dist/aptos/command.d.ts +3 -2
  8. package/dist/common.cjs +0 -3
  9. package/dist/common.d.ts +0 -4
  10. package/dist/controller.cjs +12 -7
  11. package/dist/controller.d.ts +1 -1
  12. package/dist/evm/balance-tracker.cjs +4 -4
  13. package/dist/evm/balance-tracker.d.ts +2 -2
  14. package/dist/evm/command.cjs +89 -40
  15. package/dist/evm/command.d.ts +12 -6
  16. package/dist/evm/evm.cjs +162 -58
  17. package/dist/evm/evm.d.ts +13 -8
  18. package/dist/evm/gas-price.cjs +114 -0
  19. package/dist/evm/gas-price.d.ts +33 -0
  20. package/dist/evm/pyth-contract.cjs +2 -2
  21. package/dist/evm/super-wallet.cjs +4 -4
  22. package/dist/evm/super-wallet.d.ts +1 -1
  23. package/dist/fuel/command.cjs +17 -14
  24. package/dist/fuel/command.d.ts +3 -2
  25. package/dist/fuel/fuel.d.ts +2 -2
  26. package/dist/injective/command.cjs +26 -23
  27. package/dist/injective/command.d.ts +5 -4
  28. package/dist/injective/injective.cjs +11 -11
  29. package/dist/injective/injective.d.ts +2 -3
  30. package/dist/interface.d.ts +1 -1
  31. package/dist/metrics.cjs +57 -35
  32. package/dist/metrics.d.ts +10 -5
  33. package/dist/near/command.cjs +22 -19
  34. package/dist/near/command.d.ts +5 -4
  35. package/dist/near/near.cjs +17 -16
  36. package/dist/near/near.d.ts +1 -1
  37. package/dist/options.cjs +36 -26
  38. package/dist/options.d.ts +3 -0
  39. package/dist/price-config.cjs +15 -15
  40. package/dist/pyth-price-listener.cjs +5 -3
  41. package/dist/pyth-price-listener.d.ts +2 -3
  42. package/dist/solana/balance-tracker.cjs +5 -5
  43. package/dist/solana/balance-tracker.d.ts +2 -2
  44. package/dist/solana/command.cjs +75 -60
  45. package/dist/solana/command.d.ts +13 -10
  46. package/dist/solana/solana.cjs +5 -5
  47. package/dist/solana/solana.d.ts +4 -4
  48. package/dist/sui/balance-tracker.cjs +6 -6
  49. package/dist/sui/balance-tracker.d.ts +5 -5
  50. package/dist/sui/command.cjs +84 -47
  51. package/dist/sui/command.d.ts +10 -6
  52. package/dist/sui/sui.cjs +159 -106
  53. package/dist/sui/sui.d.ts +31 -14
  54. package/dist/ton/command.cjs +17 -14
  55. package/dist/ton/command.d.ts +3 -2
  56. package/dist/ton/ton.d.ts +2 -3
  57. package/dist/utils.d.ts +1 -2
  58. package/package.json +127 -131
package/README.md CHANGED
@@ -104,8 +104,19 @@ pnpm run start evm --endpoint wss://example-rpc.com \
104
104
  [--override-gas-price-multiplier 1.1] \
105
105
  [--override-gas-price-multiplier-cap 5] \
106
106
  [--gas-limit 1000000] \
107
+ [--gas-pricing-strategy eip1559] \
108
+ [--base-fee-multiplier 1.2] \
109
+ [--priority-fee-multiplier 1] \
107
110
  [--gas-price 160000000]
108
111
 
112
+ # The EVM pusher prices transactions with the `eip1559` strategy by default. It
113
+ # computes `maxFeePerGas` from the chain base fee (padded by `--base-fee-multiplier`
114
+ # to leave headroom for the base fee growing between blocks) plus an estimated
115
+ # priority fee (scaled by `--priority-fee-multiplier`). This avoids the deprecated
116
+ # `eth_gasPrice` RPC, which some RPCs mishandle. For chains that do not support
117
+ # EIP-1559 transactions, pass `--gas-pricing-strategy legacy`, which falls back to
118
+ # the single `gasPrice` model and where `--gas-price` / `--custom-gas-station` apply.
119
+
109
120
  # For Injective
110
121
  pnpm run start injective --grpc-endpoint https://grpc-endpoint.com \
111
122
  --pyth-contract-address inj1z60tg0... --price-service-endpoint "https://example-hermes-rpc.com" \
@@ -129,19 +140,27 @@ pnpm run start aptos --endpoint https://fullnode.testnet.aptoslabs.com/v1 \
129
140
 
130
141
  # For Sui
131
142
  pnpm run start sui \
132
- --endpoint https://sui-testnet-rpc.allthatnode.com \
133
- --pyth-package-id 0x975e063f398f720af4f33ec06a927f14ea76ca24f7f8dd544aa62ab9d5d15f44 \
134
- --pyth-state-id 0xd8afde3a48b4ff7212bd6829a150f43f59043221200d63504d981f62bff2e27a \
135
- --wormhole-package-id 0xcc029e2810f17f9f43f52262f40026a71fbdca40ed3803ad2884994361910b7e \
136
- --wormhole-state-id 0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02 \
137
- --price-feed-to-price-info-object-table-id 0xf8929174008c662266a1adde78e1e8e33016eb7ad37d379481e860b911e40ed5 \
143
+ --endpoint https://fullnode.testnet.sui.io:443 \
144
+ --network testnet \
145
+ --pyth-package-id 0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837 \
146
+ --pyth-state-id 0x243759059f4c3111179da5878c12f68d612c21a8d54d85edc86164bb18be1c7c \
147
+ --wormhole-package-id 0x21473617f3565d704aa67be73ea41243e9e34a42d434c31f8182c67ba01ccf49 \
148
+ --wormhole-state-id 0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790 \
149
+ --price-feed-to-price-info-object-table-id 0xcb858b77d8068c6c8c0d8a4ddfba95053268e4a31f8ecc49adccc4ec1570d3a7 \
138
150
  --price-service-endpoint https://example-hermes-rpc.com \
139
151
  --mnemonic-file ./mnemonic \
140
152
  --price-config-file ./price-config.beta.sample.yaml \
153
+ [--endpoint-type json-rpc] \
141
154
  [--pushing-frequency 10] \
142
155
  [--polling-frequency 5] \
143
156
  [--num-gas-objects 30]
144
157
 
158
+ # `--endpoint-type` selects the Sui RPC transport: `json-rpc` (default) or `grpc`.
159
+ # Sui Foundation is deprecating JSON-RPC (public endpoints off July 2026, removed
160
+ # from full nodes by mid-Oct 2026), so set `--endpoint-type grpc` and point
161
+ # `--endpoint` at a gRPC-web base URL to migrate. `grpc` uses @mysten/sui's
162
+ # experimental SuiGrpcClient.
163
+
145
164
  # For Near
146
165
  pnpm run start near \
147
166
  --node-url https://rpc.testnet.near.org \
@@ -182,7 +201,9 @@ pnpm run start solana \
182
201
 
183
202
 
184
203
 
185
- # Or, run the price pusher docker image instead of building from the source
204
+ # Or, run the price pusher docker image instead of building from the source.
205
+ # Note: There is no :latest tag. Find available version tags at:
206
+ # https://gallery.ecr.aws/pyth-network/xc-price-pusher
186
207
  docker run public.ecr.aws/pyth-network/xc-price-pusher:v<version> -- npm run start -- <above-arguments>
187
208
  ```
188
209
 
@@ -36,18 +36,18 @@ class AptosPriceListener extends _interface.ChainPriceListener {
36
36
  // If undefined, there has been some change and we would need to update accordingly.
37
37
  const handle = res.data.info.handle;
38
38
  const priceItemRes = await client.getTableItem(handle, {
39
- key_type: `${this.pythModule}::price_identifier::PriceIdentifier`,
40
- value_type: `${this.pythModule}::price_info::PriceInfo`,
41
39
  key: {
42
40
  bytes: priceId
43
- }
41
+ },
42
+ key_type: `${this.pythModule}::price_identifier::PriceIdentifier`,
43
+ value_type: `${this.pythModule}::price_info::PriceInfo`
44
44
  });
45
45
  const multiplier = priceItemRes.price_feed.price.price.negative === true ? -1 : 1;
46
46
  const price = multiplier * Number(priceItemRes.price_feed.price.price.magnitude);
47
47
  this.logger.debug(`Polled an Aptos on-chain price for feed ${this.priceIdToAlias.get(priceId) ?? ""} (${priceId}).`);
48
48
  return {
49
- price: price.toString(),
50
49
  conf: priceItemRes.price_feed.price.conf,
50
+ price: price.toString(),
51
51
  publishTime: Number(priceItemRes.price_feed.price.timestamp)
52
52
  };
53
53
  } catch (error) {
@@ -110,11 +110,11 @@ class AptosPricePusher {
110
110
  const client = new _aptos.AptosClient(this.endpoint);
111
111
  const sequenceNumber = await this.tryGetNextSequenceNumber(client, account);
112
112
  const rawTx = await client.generateTransaction(account.address(), {
113
- function: `${this.pythContractAddress}::pyth::update_price_feeds_with_funder`,
114
- type_arguments: [],
115
113
  arguments: [
116
114
  priceFeedUpdateData
117
- ]
115
+ ],
116
+ function: `${this.pythContractAddress}::pyth::update_price_feeds_with_funder`,
117
+ type_arguments: []
118
118
  }, {
119
119
  sequence_number: sequenceNumber.toFixed(0)
120
120
  });
@@ -1,4 +1,4 @@
1
- import { HermesClient } from "@pythnetwork/hermes-client";
1
+ import type { HermesClient } from "@pythnetwork/hermes-client";
2
2
  import type { Logger } from "pino";
3
3
  import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
4
4
  import { ChainPriceListener } from "../interface.js";
@@ -30,8 +30,8 @@ class AptosBalanceTracker extends _interface.BaseBalanceTracker {
30
30
  })
31
31
  });
32
32
  this.client = new _tssdk.Aptos(new _tssdk.AptosConfig({
33
- network: _tssdk.Network.CUSTOM,
34
- fullnode: config.endpoint
33
+ fullnode: config.endpoint,
34
+ network: _tssdk.Network.CUSTOM
35
35
  }));
36
36
  this.aptosAddress = config.address;
37
37
  // APT has 8 decimal places by default
@@ -62,12 +62,12 @@ class AptosBalanceTracker extends _interface.BaseBalanceTracker {
62
62
  }
63
63
  function createAptosBalanceTracker(params) {
64
64
  return new AptosBalanceTracker({
65
- endpoint: params.endpoint,
66
65
  address: params.address,
67
- network: params.network,
68
- updateInterval: params.updateInterval,
69
- metrics: params.metrics,
66
+ decimals: params.decimals,
67
+ endpoint: params.endpoint,
70
68
  logger: params.logger,
71
- decimals: params.decimals
69
+ metrics: params.metrics,
70
+ network: params.network,
71
+ updateInterval: params.updateInterval
72
72
  });
73
73
  }
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from "pino";
2
2
  import type { BaseBalanceTrackerConfig, IBalanceTracker } from "../interface.js";
3
3
  import { BaseBalanceTracker } from "../interface.js";
4
- import { PricePusherMetrics } from "../metrics.js";
4
+ import type { PricePusherMetrics } from "../metrics.js";
5
5
  import type { DurationInSeconds } from "../utils.js";
6
6
  /**
7
7
  * Aptos-specific configuration for balance tracker
@@ -17,8 +17,8 @@ const _metrics = require("../metrics.cjs");
17
17
  const _options = /*#__PURE__*/ _interop_require_wildcard(require("../options.cjs"));
18
18
  const _priceconfig = require("../price-config.cjs");
19
19
  const _pythpricelistener = require("../pyth-price-listener.cjs");
20
- const _aptos1 = require("./aptos.cjs");
21
20
  const _utils = require("../utils.cjs");
21
+ const _aptos1 = require("./aptos.cjs");
22
22
  const _balancetracker = require("./balance-tracker.cjs");
23
23
  function _interop_require_default(obj) {
24
24
  return obj && obj.__esModule ? obj : {
@@ -67,22 +67,21 @@ function _interop_require_wildcard(obj, nodeInterop) {
67
67
  return newObj;
68
68
  }
69
69
  const _default = {
70
- command: "aptos",
71
- describe: "run price pusher for aptos",
72
70
  builder: {
73
71
  endpoint: {
74
72
  description: "RPC endpoint endpoint URL for aptos. The pusher will periodically" + "poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument.",
75
- type: "string",
76
- required: true
73
+ required: true,
74
+ type: "string"
77
75
  },
78
76
  "override-gas-price-multiplier": {
77
+ default: 2,
79
78
  description: "Multiply the gas price by this number if the transaction is not landing to override it. Default 2",
80
- type: "number",
81
79
  required: false,
82
- default: 2
80
+ type: "number"
83
81
  },
84
82
  ..._options.priceConfigFile,
85
83
  ..._options.priceServiceEndpoint,
84
+ ..._options.hermesAccessToken,
86
85
  ..._options.mnemonicFile,
87
86
  ..._options.pythContractAddress,
88
87
  ..._options.pollingFrequency,
@@ -92,14 +91,18 @@ const _default = {
92
91
  ..._options.enableMetrics,
93
92
  ..._options.metricsPort
94
93
  },
94
+ command: "aptos",
95
+ describe: "run price pusher for aptos",
95
96
  handler: async function(argv) {
96
97
  // FIXME: type checks for this
97
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, overrideGasPriceMultiplier, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
98
+ const { endpoint, priceConfigFile, priceServiceEndpoint, hermesAccessToken, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, overrideGasPriceMultiplier, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
98
99
  const logger = (0, _pino.default)({
99
100
  level: logLevel
100
101
  });
101
102
  const priceConfigs = (0, _priceconfig.readPriceConfigFile)(priceConfigFile);
102
- const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint);
103
+ const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint, {
104
+ accessToken: hermesAccessToken
105
+ });
103
106
  // Initialize metrics if enabled
104
107
  let metrics;
105
108
  if (enableMetrics) {
@@ -113,8 +116,8 @@ const _default = {
113
116
  const account = _aptos.AptosAccount.fromDerivePath(_aptos1.APTOS_ACCOUNT_HD_PATH, mnemonic);
114
117
  logger.info(`Pushing from account address: ${account.address()}`);
115
118
  let priceItems = priceConfigs.map(({ id, alias })=>({
116
- id,
117
- alias
119
+ alias,
120
+ id
118
121
  }));
119
122
  // Better to filter out invalid price items before creating the pyth listener
120
123
  const { existingPriceItems, invalidPriceItems } = await (0, _utils.filterInvalidPriceItems)(hermesClient, priceItems);
@@ -138,20 +141,20 @@ const _default = {
138
141
  }, {
139
142
  level: controllerLogLevel
140
143
  }), {
141
- pushingFrequency,
142
- metrics: metrics
144
+ metrics: metrics,
145
+ pushingFrequency
143
146
  });
144
147
  // Create and start the balance tracker if metrics are enabled
145
148
  if (metrics) {
146
149
  const balanceTracker = (0, _balancetracker.createAptosBalanceTracker)({
147
150
  address: account.address().toString(),
148
151
  endpoint,
149
- network: "aptos",
150
- updateInterval: pushingFrequency,
151
- metrics,
152
152
  logger: logger.child({
153
153
  module: "AptosBalanceTracker"
154
- })
154
+ }),
155
+ metrics,
156
+ network: "aptos",
157
+ updateInterval: pushingFrequency
155
158
  });
156
159
  // Start the balance tracker
157
160
  await balanceTracker.start();
@@ -1,7 +1,5 @@
1
1
  import type { Options } from "yargs";
2
2
  declare const _default: {
3
- command: string;
4
- describe: string;
5
3
  builder: {
6
4
  "metrics-port": Options;
7
5
  "enable-metrics": Options;
@@ -11,11 +9,14 @@ declare const _default: {
11
9
  "polling-frequency": Options;
12
10
  "pyth-contract-address": Options;
13
11
  "mnemonic-file": Options;
12
+ "hermes-access-token": Options;
14
13
  "price-service-endpoint": Options;
15
14
  "price-config-file": Options;
16
15
  endpoint: Options;
17
16
  "override-gas-price-multiplier": Options;
18
17
  };
18
+ command: string;
19
+ describe: string;
19
20
  handler: (argv: any) => Promise<void>;
20
21
  };
21
22
  export default _default;
package/dist/common.cjs CHANGED
@@ -1,4 +1 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
package/dist/common.d.ts CHANGED
@@ -1,4 +0,0 @@
1
- export type PushAttempt = {
2
- nonce: number;
3
- gasPrice: number;
4
- };
@@ -28,6 +28,9 @@ class Controller {
28
28
  this.metrics = config.metrics;
29
29
  // Set the number of price feeds if metrics are enabled
30
30
  this.metrics?.setPriceFeedsTotal(this.priceConfigs.length);
31
+ // Report unhealthy on /live if no loop iteration completes within a few
32
+ // pushing cycles (min 60s), so the k8s livenessProbe restarts a hung pod.
33
+ this.metrics?.setLivenessThresholdSeconds(Math.max(this.pushingFrequency * 5, 60));
31
34
  }
32
35
  async start() {
33
36
  // start the listeners
@@ -49,13 +52,13 @@ class Controller {
49
52
  const targetLatestPrice = this.targetPriceListener.getLatestPriceInfo(priceId);
50
53
  const sourceLatestPrice = this.sourcePriceListener.getLatestPriceInfo(priceId);
51
54
  if (this.metrics && targetLatestPrice && sourceLatestPrice) {
52
- this.metrics.updateTimestamps(priceId, alias, targetLatestPrice.publishTime, sourceLatestPrice.publishTime, priceConfig.timeDifference);
53
- this.metrics.updatePriceValues(priceId, alias, sourceLatestPrice.price, targetLatestPrice.price);
55
+ this.metrics.updateTimestamps(alias, targetLatestPrice.publishTime, sourceLatestPrice.publishTime, priceConfig.timeDifference);
56
+ this.metrics.updatePriceValues(alias, sourceLatestPrice.price, targetLatestPrice.price);
54
57
  }
55
58
  const priceShouldUpdate = (0, _priceconfig.shouldUpdate)(priceConfig, sourceLatestPrice, targetLatestPrice, this.logger);
56
59
  // Record update condition in metrics
57
60
  if (this.metrics) {
58
- this.metrics.recordUpdateCondition(priceId, alias, priceShouldUpdate);
61
+ this.metrics.recordUpdateCondition(alias, priceShouldUpdate);
59
62
  }
60
63
  if (priceShouldUpdate == _priceconfig.UpdateCondition.YES) {
61
64
  pushThresholdMet = true;
@@ -68,8 +71,8 @@ class Controller {
68
71
  if (pushThresholdMet) {
69
72
  this.logger.info({
70
73
  priceIds: pricesToPush.map((priceConfig)=>({
71
- id: priceConfig.id,
72
- alias: priceConfig.alias
74
+ alias: priceConfig.alias,
75
+ id: priceConfig.id
73
76
  }))
74
77
  }, "Some of the checks triggered pushing update. Will push the updates for some feeds.");
75
78
  // note that the priceIds are without leading "0x"
@@ -80,7 +83,7 @@ class Controller {
80
83
  if (this.metrics) {
81
84
  for (const config of pricesToPush){
82
85
  const triggerValue = (0, _priceconfig.shouldUpdate)(config, this.sourcePriceListener.getLatestPriceInfo(config.id), this.targetPriceListener.getLatestPriceInfo(config.id), this.logger) === _priceconfig.UpdateCondition.YES ? "yes" : "early";
83
- this.metrics.recordPriceUpdate(config.id, config.alias, triggerValue);
86
+ this.metrics.recordPriceUpdate(config.alias, triggerValue);
84
87
  }
85
88
  }
86
89
  } catch (error) {
@@ -92,13 +95,15 @@ class Controller {
92
95
  if (this.metrics) {
93
96
  for (const config of pricesToPush){
94
97
  const triggerValue = (0, _priceconfig.shouldUpdate)(config, this.sourcePriceListener.getLatestPriceInfo(config.id), this.targetPriceListener.getLatestPriceInfo(config.id), this.logger) === _priceconfig.UpdateCondition.YES ? "yes" : "early";
95
- this.metrics.recordPriceUpdateError(config.id, config.alias, triggerValue);
98
+ this.metrics.recordPriceUpdateError(config.alias, triggerValue);
96
99
  }
97
100
  }
98
101
  }
99
102
  } else {
100
103
  this.logger.info("None of the checks were triggered. No push needed.");
101
104
  }
105
+ // Liveness heartbeat: mark this loop iteration as completed.
106
+ this.metrics?.recordLoopIteration();
102
107
  await (0, _utils.sleep)(this.pushingFrequency * 1000);
103
108
  }
104
109
  }
@@ -1,6 +1,6 @@
1
1
  import type { Logger } from "pino";
2
2
  import type { IPriceListener, IPricePusher } from "./interface.js";
3
- import { PricePusherMetrics } from "./metrics.js";
3
+ import type { PricePusherMetrics } from "./metrics.js";
4
4
  import type { PriceConfig } from "./price-config.js";
5
5
  import type { DurationInSeconds } from "./utils.js";
6
6
  export declare class Controller {
@@ -48,11 +48,11 @@ class EvmBalanceTracker extends _interface.BaseBalanceTracker {
48
48
  }
49
49
  function createEvmBalanceTracker(params) {
50
50
  return new EvmBalanceTracker({
51
- client: params.client,
52
51
  address: params.address,
53
- network: params.network,
54
- updateInterval: params.updateInterval,
52
+ client: params.client,
53
+ logger: params.logger,
55
54
  metrics: params.metrics,
56
- logger: params.logger
55
+ network: params.network,
56
+ updateInterval: params.updateInterval
57
57
  });
58
58
  }
@@ -1,9 +1,9 @@
1
1
  import type { Logger } from "pino";
2
- import type { SuperWalletClient } from "./super-wallet.js";
3
2
  import type { BaseBalanceTrackerConfig, IBalanceTracker } from "../interface.js";
4
3
  import { BaseBalanceTracker } from "../interface.js";
5
- import { PricePusherMetrics } from "../metrics.js";
4
+ import type { PricePusherMetrics } from "../metrics.js";
6
5
  import type { DurationInSeconds } from "../utils.js";
6
+ import type { SuperWalletClient } from "./super-wallet.js";
7
7
  /**
8
8
  * EVM-specific configuration for balance tracker
9
9
  */
@@ -12,16 +12,16 @@ const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
12
12
  const _hermesclient = require("@pythnetwork/hermes-client");
13
13
  const _pino = /*#__PURE__*/ _interop_require_default(require("pino"));
14
14
  const _controller = require("../controller.cjs");
15
+ const _metrics = require("../metrics.cjs");
15
16
  const _options = /*#__PURE__*/ _interop_require_wildcard(require("../options.cjs"));
16
17
  const _priceconfig = require("../price-config.cjs");
17
18
  const _pythpricelistener = require("../pyth-price-listener.cjs");
19
+ const _utils = require("../utils.cjs");
20
+ const _balancetracker = require("./balance-tracker.cjs");
18
21
  const _customgasstation = require("./custom-gas-station.cjs");
19
22
  const _evm = require("./evm.cjs");
20
23
  const _pythcontract = require("./pyth-contract.cjs");
21
24
  const _superwallet = require("./super-wallet.cjs");
22
- const _metrics = require("../metrics.cjs");
23
- const _utils = require("../utils.cjs");
24
- const _balancetracker = require("./balance-tracker.cjs");
25
25
  function _interop_require_default(obj) {
26
26
  return obj && obj.__esModule ? obj : {
27
27
  default: obj
@@ -69,58 +69,90 @@ function _interop_require_wildcard(obj, nodeInterop) {
69
69
  return newObj;
70
70
  }
71
71
  const _default = {
72
- command: "evm",
73
- describe: "run price pusher for evm",
74
72
  builder: {
75
- endpoint: {
76
- description: "RPC endpoint URL for evm network. If you provide a normal HTTP endpoint, the pusher " + "will periodically poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument. If you provide a websocket RPC " + "endpoint (`ws[s]://...`), the price pusher will use event subscriptions to read " + "the current EVM price in addition to polling. ",
77
- type: "string",
78
- required: true
73
+ "base-fee-multiplier": {
74
+ default: 1.2,
75
+ description: "[eip1559 strategy only] Multiplier applied to the chain base fee when computing " + "maxFeePerGas. Values above 1 leave headroom for the base fee to grow between blocks. " + "Default to 1.2",
76
+ required: false,
77
+ type: "number"
79
78
  },
80
79
  "custom-gas-station": {
81
80
  description: "If using a custom gas station, chainId of custom gas station to use",
82
- type: "number",
83
- required: false
81
+ required: false,
82
+ type: "number"
84
83
  },
85
- "tx-speed": {
86
- description: "txSpeed for custom gas station. choose between 'slow'|'standard'|'fast'",
84
+ endpoint: {
85
+ description: "RPC endpoint URL for evm network. If you provide a normal HTTP endpoint, the pusher " + "will periodically poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument. If you provide a websocket RPC " + "endpoint (`ws[s]://...`), the price pusher will use event subscriptions to read " + "the current EVM price in addition to polling. ",
86
+ required: true,
87
+ type: "string"
88
+ },
89
+ "gas-limit": {
90
+ description: "Gas limit for the transaction",
91
+ required: false,
92
+ type: "number"
93
+ },
94
+ "gas-price": {
95
+ description: "[legacy strategy only] Override the gas price (in wei) that would be received from the RPC",
96
+ required: false,
97
+ type: "number"
98
+ },
99
+ "gas-pricing-strategy": {
87
100
  choices: [
88
- "slow",
89
- "standard",
90
- "fast"
101
+ "eip1559",
102
+ "legacy"
91
103
  ],
104
+ default: "eip1559",
105
+ description: "How to price transactions. 'eip1559' (default and recommended) sources the " + "gas from the chain base fee and a priority fee, which is more robust as it " + "avoids the deprecated eth_gasPrice RPC. Use 'legacy' for chains that do not " + "support eip1559 transactions.",
92
106
  required: false
93
107
  },
94
108
  "override-gas-price-multiplier": {
109
+ default: 1.1,
95
110
  description: "Multiply the previous gas price by this number if the transaction is not landing to override. " + "Please note that the gas price can grow exponentially on consecutive failures; " + "to set a cap on the multiplier, use the `override-gas-price-multiplier-cap` option." + "Default to 1.1",
96
- type: "number",
97
111
  required: false,
98
- default: 1.1
112
+ type: "number"
99
113
  },
100
114
  "override-gas-price-multiplier-cap": {
115
+ default: 5,
101
116
  description: "Maximum gas price multiplier to use in override compared to the RPC returned " + "gas price. Default to 5",
102
- type: "number",
103
117
  required: false,
104
- default: 5
118
+ type: "number"
105
119
  },
106
- "gas-limit": {
107
- description: "Gas limit for the transaction",
108
- type: "number",
109
- required: false
120
+ "priority-fee-multiplier": {
121
+ default: 1,
122
+ description: "[eip1559 strategy only] Multiplier applied to the priority fee (tip) estimated from " + "the RPC. Default to 1",
123
+ required: false,
124
+ type: "number"
110
125
  },
111
- "gas-price": {
112
- description: "Override the gas price that would be received from the RPC",
113
- type: "number",
126
+ "receipt-poll-interval": {
127
+ default: 2,
128
+ description: "How often (in seconds) to poll for a sent transaction's receipt while waiting for " + "it to land. Default to 2",
129
+ required: false,
130
+ type: "number"
131
+ },
132
+ "receipt-wait-timeout": {
133
+ default: 60,
134
+ description: "Maximum time (in seconds) to poll for a sent transaction's receipt before giving " + "up. Bounds the wait so a transaction that never lands cannot be polled forever. " + "Default to 60",
135
+ required: false,
136
+ type: "number"
137
+ },
138
+ "tx-speed": {
139
+ choices: [
140
+ "slow",
141
+ "standard",
142
+ "fast"
143
+ ],
144
+ description: "txSpeed for custom gas station. choose between 'slow'|'standard'|'fast'",
114
145
  required: false
115
146
  },
116
147
  "update-fee-multiplier": {
148
+ default: 1,
117
149
  description: "Multiplier for the fee to update the price. It is useful in networks " + "such as Hedera where setting on-chain getUpdateFee as the transaction value " + "won't work. Default to 1",
118
- type: "number",
119
150
  required: false,
120
- default: 1
151
+ type: "number"
121
152
  },
122
153
  ..._options.priceConfigFile,
123
154
  ..._options.priceServiceEndpoint,
155
+ ..._options.hermesAccessToken,
124
156
  ..._options.mnemonicFile,
125
157
  ..._options.pythContractAddress,
126
158
  ..._options.pollingFrequency,
@@ -130,18 +162,22 @@ const _default = {
130
162
  ..._options.enableMetrics,
131
163
  ..._options.metricsPort
132
164
  },
165
+ command: "evm",
166
+ describe: "run price pusher for evm",
133
167
  handler: async function(argv) {
134
168
  // FIXME: type checks for this
135
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, customGasStation, txSpeed, overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, gasLimit, gasPrice, updateFeeMultiplier, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
169
+ const { endpoint, priceConfigFile, priceServiceEndpoint, hermesAccessToken, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, customGasStation, txSpeed, overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, gasLimit, gasPricingStrategy, gasPrice, baseFeeMultiplier, priorityFeeMultiplier, updateFeeMultiplier, receiptPollInterval, receiptWaitTimeout, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
136
170
  const logger = (0, _pino.default)({
137
171
  level: logLevel
138
172
  });
139
173
  const priceConfigs = (0, _priceconfig.readPriceConfigFile)(priceConfigFile);
140
- const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint);
174
+ const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint, {
175
+ accessToken: hermesAccessToken
176
+ });
141
177
  const mnemonic = _nodefs.default.readFileSync(mnemonicFile, "utf8").trim();
142
178
  let priceItems = priceConfigs.map(({ id, alias })=>({
143
- id,
144
- alias
179
+ alias,
180
+ id
145
181
  }));
146
182
  // Better to filter out invalid price items before creating the pyth listener
147
183
  const { existingPriceItems, invalidPriceItems } = await (0, _utils.filterInvalidPriceItems)(hermesClient, priceItems);
@@ -176,26 +212,39 @@ const _default = {
176
212
  const gasStation = (0, _customgasstation.getCustomGasStation)(logger.child({
177
213
  module: "CustomGasStation"
178
214
  }), customGasStation, txSpeed);
215
+ if (gasPricingStrategy === "eip1559" && (gasStation || gasPrice)) {
216
+ logger.warn("`--custom-gas-station`/`--tx-speed` and `--gas-price` only apply to the 'legacy' " + "gas pricing strategy and will be ignored. Use `--base-fee-multiplier` and " + "`--priority-fee-multiplier` to tune the 'eip1559' strategy.");
217
+ }
218
+ const gasPriceConfig = {
219
+ baseFeeMultiplier,
220
+ customGasStation: gasStation,
221
+ gasPrice,
222
+ priorityFeeMultiplier,
223
+ strategy: gasPricingStrategy
224
+ };
179
225
  const evmPusher = new _evm.EvmPricePusher(hermesClient, client, pythContract, logger.child({
180
226
  module: "EvmPricePusher"
181
- }), overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, updateFeeMultiplier, gasLimit, gasStation, gasPrice);
227
+ }), overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, updateFeeMultiplier, gasPriceConfig, gasLimit, // CLI values are in seconds; the pusher expects milliseconds. Defaults
228
+ // (60s / 2s) reproduce the pre-flag behaviour, so existing deployments
229
+ // that don't set these flags are unaffected.
230
+ receiptWaitTimeout * 1000, receiptPollInterval * 1000);
182
231
  const controller = new _controller.Controller(priceConfigs, pythListener, evmListener, evmPusher, logger.child({
183
232
  module: "Controller"
184
233
  }, {
185
234
  level: controllerLogLevel
186
235
  }), {
187
- pushingFrequency,
188
- metrics: metrics
236
+ metrics: metrics,
237
+ pushingFrequency
189
238
  });
190
239
  // Create and start the balance tracker if metrics are enabled
191
240
  if (metrics) {
192
241
  const balanceTracker = (0, _balancetracker.createEvmBalanceTracker)({
193
- client,
194
242
  address: client.account.address,
195
- network: await client.getChainId().then((id)=>id.toString()),
196
- updateInterval: pushingFrequency,
243
+ client,
244
+ logger,
197
245
  metrics,
198
- logger
246
+ network: await client.getChainId().then((id)=>id.toString()),
247
+ updateInterval: pushingFrequency
199
248
  });
200
249
  // Start the balance tracker
201
250
  await balanceTracker.start();
@@ -1,7 +1,5 @@
1
1
  import type { Options } from "yargs";
2
2
  declare const _default: {
3
- command: string;
4
- describe: string;
5
3
  builder: {
6
4
  "metrics-port": Options;
7
5
  "enable-metrics": Options;
@@ -11,17 +9,25 @@ declare const _default: {
11
9
  "polling-frequency": Options;
12
10
  "pyth-contract-address": Options;
13
11
  "mnemonic-file": Options;
12
+ "hermes-access-token": Options;
14
13
  "price-service-endpoint": Options;
15
14
  "price-config-file": Options;
16
- endpoint: Options;
15
+ "base-fee-multiplier": Options;
17
16
  "custom-gas-station": Options;
18
- "tx-speed": Options;
19
- "override-gas-price-multiplier": Options;
20
- "override-gas-price-multiplier-cap": Options;
17
+ endpoint: Options;
21
18
  "gas-limit": Options;
22
19
  "gas-price": Options;
20
+ "gas-pricing-strategy": Options;
21
+ "override-gas-price-multiplier": Options;
22
+ "override-gas-price-multiplier-cap": Options;
23
+ "priority-fee-multiplier": Options;
24
+ "receipt-poll-interval": Options;
25
+ "receipt-wait-timeout": Options;
26
+ "tx-speed": Options;
23
27
  "update-fee-multiplier": Options;
24
28
  };
29
+ command: string;
30
+ describe: string;
25
31
  handler: (argv: any) => Promise<void>;
26
32
  };
27
33
  export default _default;