@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
@@ -38,7 +38,7 @@ class SolanaPriceListener extends _interface.ChainPriceListener {
38
38
  try {
39
39
  const blockTime = await this.pythSolanaReceiver.connection.getBlockTime(slot);
40
40
  if ((blockTime === null || blockTime < Date.now() / 1000 - HEALTH_CHECK_TIMEOUT_SECONDS) && blockTime !== null) {
41
- this.logger.info(`Solana connection is behind by ${(Date.now() / 1000 - blockTime).toString()} seconds`);
41
+ this.logger.warn(`Solana connection is behind by ${(Date.now() / 1000 - blockTime).toString()} seconds`);
42
42
  }
43
43
  } catch (error) {
44
44
  this.logger.error({
@@ -115,7 +115,7 @@ class SolanaPricePusher {
115
115
  });
116
116
  try {
117
117
  const signatures = await (0, _solanautils.sendTransactions)(transactions, this.pythSolanaReceiver.connection, this.pythSolanaReceiver.wallet);
118
- this.logger.info({
118
+ this.logger.debug({
119
119
  signatures
120
120
  }, "updatePriceFeed successful");
121
121
  } catch (error) {
@@ -153,7 +153,7 @@ class SolanaPricePusherJito {
153
153
  try {
154
154
  const response = await fetch("https://bundles.jito.wtf/api/v1/bundles/tip_floor");
155
155
  if (!response.ok) {
156
- this.logger.error({
156
+ this.logger.warn({
157
157
  status: response.status,
158
158
  statusText: response.statusText
159
159
  }, "getRecentJitoTips http request failed");
@@ -164,7 +164,7 @@ class SolanaPricePusherJito {
164
164
  return Math.floor(Number(data[0].landed_tips_50th_percentile) * _web3.LAMPORTS_PER_SOL);
165
165
  } catch (error) {
166
166
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
167
- this.logger.error({
167
+ this.logger.warn({
168
168
  err: error
169
169
  }, "getRecentJitoTips failed");
170
170
  return undefined;
@@ -174,7 +174,7 @@ class SolanaPricePusherJito {
174
174
  const recentJitoTip = await this.getRecentJitoTipLamports();
175
175
  const jitoTip = this.dynamicJitoTips && recentJitoTip !== undefined ? Math.max(this.defaultJitoTipLamports, recentJitoTip) : this.defaultJitoTipLamports;
176
176
  const cappedJitoTip = Math.min(jitoTip, this.maxJitoTipLamports);
177
- this.logger.info({
177
+ this.logger.debug({
178
178
  cappedJitoTip
179
179
  }, "using jito tip of");
180
180
  let priceFeedUpdateData;
@@ -1,7 +1,7 @@
1
- import { HermesClient } from "@pythnetwork/hermes-client";
2
- import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
3
- import { AddressLookupTableAccount } from "@solana/web3.js";
4
- import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
1
+ import type { HermesClient } from "@pythnetwork/hermes-client";
2
+ import type { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
3
+ import type { AddressLookupTableAccount } from "@solana/web3.js";
4
+ import type { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
5
5
  import type { Logger } from "pino";
6
6
  import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
7
7
  import { ChainPriceListener } from "../interface.js";
@@ -32,11 +32,11 @@ class SuiBalanceTracker extends _interface.BaseBalanceTracker {
32
32
  * Sui-specific implementation of balance update
33
33
  */ async updateBalance() {
34
34
  try {
35
- const balance = await this.client.getBalance({
35
+ const { balance } = await this.client.core.getBalance({
36
36
  owner: this.address
37
37
  });
38
38
  // Convert to a normalized number for reporting (SUI has 9 decimals)
39
- const normalizedBalance = Number(balance.totalBalance) / 1e9;
39
+ const normalizedBalance = Number(balance.balance) / 1e9;
40
40
  this.metrics.updateWalletBalance(this.address, this.network, normalizedBalance);
41
41
  this.logger.debug(`Updated Sui wallet balance: ${this.address} = ${normalizedBalance.toString()} SUI`);
42
42
  } catch (error) {
@@ -48,11 +48,11 @@ class SuiBalanceTracker extends _interface.BaseBalanceTracker {
48
48
  }
49
49
  function createSuiBalanceTracker(params) {
50
50
  return new SuiBalanceTracker({
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,15 +1,15 @@
1
- import { SuiClient } from "@mysten/sui/client";
1
+ import type { ClientWithCoreApi } from "@mysten/sui/client";
2
2
  import type { Logger } from "pino";
3
3
  import type { BaseBalanceTrackerConfig, IBalanceTracker } from "../interface.js";
4
4
  import { BaseBalanceTracker } from "../interface.js";
5
- import { PricePusherMetrics } from "../metrics.js";
5
+ import type { PricePusherMetrics } from "../metrics.js";
6
6
  import type { DurationInSeconds } from "../utils.js";
7
7
  /**
8
8
  * Sui-specific configuration for balance tracker
9
9
  */
10
10
  export type SuiBalanceTrackerConfig = {
11
- /** Sui client instance */
12
- client: SuiClient;
11
+ /** Sui client instance (JSON-RPC or gRPC) */
12
+ client: ClientWithCoreApi;
13
13
  } & BaseBalanceTrackerConfig;
14
14
  /**
15
15
  * Sui-specific implementation of the balance tracker
@@ -26,7 +26,7 @@ export declare class SuiBalanceTracker extends BaseBalanceTracker {
26
26
  * Parameters for creating a Sui balance tracker
27
27
  */
28
28
  export type CreateSuiBalanceTrackerParams = {
29
- client: SuiClient;
29
+ client: ClientWithCoreApi;
30
30
  address: string;
31
31
  network: string;
32
32
  updateInterval: DurationInSeconds;
@@ -1,4 +1,4 @@
1
- /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ "use strict";
1
+ /* biome-ignore-all lint/style/noNonNullAssertion: pre-existing; metrics wiring asserts on optional config */ /* biome-ignore-all lint/suspicious/noExplicitAny: pre-existing; yargs argv is untyped */ "use strict";
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
@@ -9,7 +9,6 @@ Object.defineProperty(exports, "default", {
9
9
  }
10
10
  });
11
11
  const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
12
- const _client = require("@mysten/sui/client");
13
12
  const _ed25519 = require("@mysten/sui/keypairs/ed25519");
14
13
  const _hermesclient = require("@pythnetwork/hermes-client");
15
14
  const _pino = /*#__PURE__*/ _interop_require_default(require("pino"));
@@ -18,9 +17,9 @@ const _metrics = require("../metrics.cjs");
18
17
  const _options = /*#__PURE__*/ _interop_require_wildcard(require("../options.cjs"));
19
18
  const _priceconfig = require("../price-config.cjs");
20
19
  const _pythpricelistener = require("../pyth-price-listener.cjs");
20
+ const _utils = require("../utils.cjs");
21
21
  const _balancetracker = require("./balance-tracker.cjs");
22
22
  const _sui = require("./sui.cjs");
23
- const _utils = require("../utils.cjs");
24
23
  function _interop_require_default(obj) {
25
24
  return obj && obj.__esModule ? obj : {
26
25
  default: obj
@@ -68,50 +67,76 @@ function _interop_require_wildcard(obj, nodeInterop) {
68
67
  return newObj;
69
68
  }
70
69
  const _default = {
71
- command: "sui",
72
- describe: "Run price pusher for sui. Most of the arguments below are" + "network specific, so there's one set of values for mainnet and" + " another for testnet. See config.sui.mainnet.sample.json for the " + "appropriate values for your network. ",
73
70
  builder: {
74
- endpoint: {
75
- description: "RPC endpoint URL for sui. The pusher will periodically" + "poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument.",
76
- type: "string",
77
- required: true
71
+ "account-index": {
72
+ default: 0,
73
+ description: "Index of the account to use derived by the mnemonic",
74
+ required: true,
75
+ type: "number"
78
76
  },
79
- "pyth-state-id": {
80
- description: "Pyth State Id. Can be found here" + "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
81
- type: "string",
82
- required: true
77
+ endpoint: {
78
+ description: "RPC endpoint URL for sui. The pusher will periodically" + "poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument. For `--endpoint-type grpc` " + "this is the gRPC-web base URL.",
79
+ required: true,
80
+ type: "string"
83
81
  },
84
- "wormhole-state-id": {
85
- description: "Wormhole State Id. Can be found here" + "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
86
- type: "string",
87
- required: true
82
+ "endpoint-type": {
83
+ choices: [
84
+ "json-rpc",
85
+ "grpc"
86
+ ],
87
+ default: "json-rpc",
88
+ description: "Transport to use for the Sui RPC endpoint. Sui Foundation is deprecating " + "JSON-RPC (public endpoints off July 2026, removed by mid-Oct 2026); use " + "`grpc` to migrate. `grpc` relies on @mysten/sui's experimental SuiGrpcClient.",
89
+ required: false,
90
+ type: "string"
88
91
  },
89
- "num-gas-objects": {
90
- description: "Number of gas objects in the pool.",
91
- type: "number",
92
+ "gas-budget": {
93
+ default: 500_000_000,
94
+ description: "Gas budget for each price update",
92
95
  required: true,
93
- default: 30
96
+ type: "number"
97
+ },
98
+ "grpc-metadata": {
99
+ default: [],
100
+ description: "Metadata header(s) sent with every gRPC request, formatted as " + "`key=value`. Repeat for multiple headers. Use this to authenticate " + "native-gRPC providers: `x-token=<secret>` for QuikNode/Ankr, " + "`x-api-key=<secret>` for BlockVision. Only used with `--endpoint-type grpc`.",
101
+ required: false,
102
+ type: "array"
94
103
  },
95
104
  "ignore-gas-objects": {
105
+ default: [],
96
106
  description: "Gas objects to ignore when merging gas objects on startup -- use this for locked objects.",
97
- type: "array",
98
107
  required: false,
99
- default: []
108
+ type: "array"
100
109
  },
101
- "gas-budget": {
102
- description: "Gas budget for each price update",
103
- type: "number",
110
+ network: {
111
+ choices: [
112
+ "mainnet",
113
+ "testnet",
114
+ "devnet",
115
+ "localnet"
116
+ ],
117
+ description: "Sui network label for the RPC client. This selects the network the " + "endpoint serves; it does not change which `--endpoint` URL is used.",
104
118
  required: true,
105
- default: 500_000_000
119
+ type: "string"
106
120
  },
107
- "account-index": {
108
- description: "Index of the account to use derived by the mnemonic",
109
- type: "number",
121
+ "num-gas-objects": {
122
+ default: 30,
123
+ description: "Number of gas objects in the pool.",
124
+ required: true,
125
+ type: "number"
126
+ },
127
+ "pyth-state-id": {
128
+ description: "Pyth State Id. Can be found here" + "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
129
+ required: true,
130
+ type: "string"
131
+ },
132
+ "wormhole-state-id": {
133
+ description: "Wormhole State Id. Can be found here" + "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
110
134
  required: true,
111
- default: 0
135
+ type: "string"
112
136
  },
113
137
  ..._options.priceConfigFile,
114
138
  ..._options.priceServiceEndpoint,
139
+ ..._options.hermesAccessToken,
115
140
  ..._options.mnemonicFile,
116
141
  ..._options.pollingFrequency,
117
142
  ..._options.pushingFrequency,
@@ -120,20 +145,34 @@ const _default = {
120
145
  ..._options.enableMetrics,
121
146
  ..._options.metricsPort
122
147
  },
148
+ command: "sui",
149
+ describe: "Run price pusher for sui. Most of the arguments below are" + "network specific, so there's one set of values for mainnet and" + " another for testnet. See config.sui.mainnet.sample.json for the " + "appropriate values for your network. ",
123
150
  handler: async function(argv) {
124
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythStateId, wormholeStateId, numGasObjects, ignoreGasObjects, gasBudget, accountIndex, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
151
+ const { endpoint, endpointType, network, priceConfigFile, priceServiceEndpoint, hermesAccessToken, mnemonicFile, pushingFrequency, pollingFrequency, pythStateId, wormholeStateId, numGasObjects, ignoreGasObjects, gasBudget, accountIndex, grpcMetadata: grpcMetadataArgs, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
125
152
  const logger = (0, _pino.default)({
126
153
  level: logLevel
127
154
  });
155
+ // Parse `--grpc-metadata key=value` pairs into the metadata map forwarded
156
+ // to the gRPC transport (e.g. the `x-token` auth header).
157
+ const grpcMetadata = {};
158
+ for (const entry of grpcMetadataArgs){
159
+ const eq = entry.indexOf("=");
160
+ if (eq === -1) {
161
+ throw new Error(`Invalid --grpc-metadata "${entry}": expected key=value`);
162
+ }
163
+ grpcMetadata[entry.slice(0, eq)] = entry.slice(eq + 1);
164
+ }
128
165
  const priceConfigs = (0, _priceconfig.readPriceConfigFile)(priceConfigFile);
129
- const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint);
166
+ const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint, {
167
+ accessToken: hermesAccessToken
168
+ });
130
169
  const mnemonic = _nodefs.default.readFileSync(mnemonicFile, "utf8").trim();
131
170
  const keypair = _ed25519.Ed25519Keypair.deriveKeypair(mnemonic, `m/44'/784'/${accountIndex}'/0'/0'`);
132
171
  const suiAddress = keypair.getPublicKey().toSuiAddress();
133
172
  logger.info(`Pushing updates from wallet address: ${suiAddress}`);
134
173
  let priceItems = priceConfigs.map(({ id, alias })=>({
135
- id,
136
- alias
174
+ alias,
175
+ id
137
176
  }));
138
177
  // Better to filter out invalid price items before creating the pyth listener
139
178
  const { existingPriceItems, invalidPriceItems } = await (0, _utils.filterInvalidPriceItems)(hermesClient, priceItems);
@@ -153,34 +192,32 @@ const _default = {
153
192
  const pythListener = new _pythpricelistener.PythPriceListener(hermesClient, priceItems, logger.child({
154
193
  module: "PythPriceListener"
155
194
  }));
156
- const suiClient = new _client.SuiClient({
157
- url: endpoint
158
- });
159
- const suiListener = new _sui.SuiPriceListener(pythStateId, wormholeStateId, endpoint, priceItems, logger.child({
195
+ const suiClient = (0, _sui.createSuiProvider)(endpointType, network, endpoint, grpcMetadata);
196
+ const suiListener = new _sui.SuiPriceListener(pythStateId, wormholeStateId, endpoint, endpointType, network, priceItems, logger.child({
160
197
  module: "SuiPriceListener"
161
198
  }), {
162
199
  pollingFrequency
163
- });
200
+ }, grpcMetadata);
164
201
  const suiPusher = await _sui.SuiPricePusher.createWithAutomaticGasPool(hermesClient, logger.child({
165
202
  module: "SuiPricePusher"
166
- }), pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects, ignoreGasObjects);
203
+ }), pythStateId, wormholeStateId, endpoint, endpointType, network, keypair, gasBudget, numGasObjects, ignoreGasObjects, grpcMetadata);
167
204
  const controller = new _controller.Controller(priceConfigs, pythListener, suiListener, suiPusher, logger.child({
168
205
  module: "Controller"
169
206
  }, {
170
207
  level: controllerLogLevel
171
208
  }), {
172
- pushingFrequency,
173
- metrics: metrics
209
+ metrics: metrics,
210
+ pushingFrequency
174
211
  });
175
212
  // Create and start the balance tracker if metrics are enabled
176
213
  if (metrics) {
177
214
  const balanceTracker = (0, _balancetracker.createSuiBalanceTracker)({
178
- client: suiClient,
179
215
  address: suiAddress,
180
- network: "sui",
181
- updateInterval: pushingFrequency,
216
+ client: suiClient,
217
+ logger,
182
218
  metrics,
183
- logger
219
+ network: "sui",
220
+ updateInterval: pushingFrequency
184
221
  });
185
222
  // Start the balance tracker
186
223
  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;
@@ -10,16 +8,22 @@ declare const _default: {
10
8
  "pushing-frequency": Options;
11
9
  "polling-frequency": Options;
12
10
  "mnemonic-file": Options;
11
+ "hermes-access-token": Options;
13
12
  "price-service-endpoint": Options;
14
13
  "price-config-file": Options;
14
+ "account-index": Options;
15
15
  endpoint: Options;
16
+ "endpoint-type": Options;
17
+ "gas-budget": Options;
18
+ "grpc-metadata": Options;
19
+ "ignore-gas-objects": Options;
20
+ network: Options;
21
+ "num-gas-objects": Options;
16
22
  "pyth-state-id": Options;
17
23
  "wormhole-state-id": Options;
18
- "num-gas-objects": Options;
19
- "ignore-gas-objects": Options;
20
- "gas-budget": Options;
21
- "account-index": Options;
22
24
  };
25
+ command: string;
26
+ describe: string;
23
27
  handler: (argv: any) => Promise<void>;
24
28
  };
25
29
  export default _default;