Skip to content

feat(price-pusher): allow multiple injective gRPC endpoints with failover#3703

Open
0xghost42 wants to merge 3 commits into
pyth-network:mainfrom
0xghost42:feat/1014-grpc-endpoints
Open

feat(price-pusher): allow multiple injective gRPC endpoints with failover#3703
0xghost42 wants to merge 3 commits into
pyth-network:mainfrom
0xghost42:feat/1014-grpc-endpoints

Conversation

@0xghost42
Copy link
Copy Markdown

@0xghost42 0xghost42 commented May 14, 2026

Summary

Closes #1014.

The injective price pusher only accepted a single --grpc-endpoint. Cosmos nodes are occasionally flaky, so operators ended up spinning up one pusher process per endpoint just to get redundancy. Maintainer (@jayantk) confirmed on the original issue that cycling chain endpoints was the right shape.

This PR makes --grpc-endpoint a fallback set: the pusher round-robins to the next endpoint whenever a gRPC call fails, walking every endpoint at most once before re-throwing.

Change

  • --grpc-endpoint is now type: "array". Accepts either repeated flag occurrences (--grpc-endpoint a --grpc-endpoint b) or comma-separated values (--grpc-endpoint a,b,c). A single endpoint still works exactly as before. Empty input rejected up front with a clear message.
  • New apps/price_pusher/src/injective/endpoint-pool.ts:
    • EndpointPool — round-robin cursor over a non-empty endpoint list, shared across every gRPC API helper in a pusher instance so a bad endpoint affects the whole pusher, not just one method.
    • withEndpointFailover(pool, fn) — runs fn(currentEndpoint), rotates on failure, walks every endpoint at most once before re-throwing the last error.
  • InjectivePriceListener + InjectivePricePusher constructors take string | readonly string[] for backward compatibility and wrap each gRPC call site (ChainGrpcWasmApi.fetchSmartContractState, ChainGrpcAuthApi.fetchAccount, TxGrpcApi.broadcast, TxGrpcApi.simulate) in withEndpointFailover.
  • README: short note on the new fallback-set syntax under the Injective example.

Verification

  • pnpm --filter @pythnetwork/price-pusher exec test-unit — 8 cases, all pass:
    • empty endpoint list rejected
    • default cursor returns first endpoint
    • round-robin rotation wraps past the end
    • single-endpoint pool: rotate is a no-op
    • withEndpointFailover returns first successful result without rotating
    • rotates and returns next-endpoint's result on single failure
    • exhausts the pool and re-throws the last error
    • respects the pool's current cursor position when starting
  • pnpm --filter @pythnetwork/price-pusher exec tsc --noEmit clean.
  • Wired test:unit into package.json scripts so the workspace test runner picks the new file up.

Out of scope

The original issue also mentioned the Pyth price service (Hermes) gRPC endpoint as a possibly useful failover target. That's a different code path (HermesClient takes a single URL today), so I kept this PR focused on the chain-endpoint case @ewoolsey named primarily. Happy to follow up on the Hermes side in a separate PR if the maintainers want it.


Open in Devin Review

@0xghost42 0xghost42 requested a review from a team as a code owner May 14, 2026 11:04
@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

7 Skipped Deployments
Project Deployment Actions Updated (UTC)
api-reference Skipped Skipped May 21, 2026 8:37am
component-library Skipped Skipped May 21, 2026 8:37am
developer-hub Skipped Skipped May 21, 2026 8:37am
entropy-explorer Skipped Skipped May 21, 2026 8:37am
insights Skipped Skipped May 21, 2026 8:37am
proposals Skipped Skipped May 21, 2026 8:37am
staking Skipped Skipped May 21, 2026 8:37am

Request Review

@vercel vercel Bot temporarily deployed to Preview – api-reference May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – staking May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – developer-hub May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – entropy-explorer May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – insights May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – component-library May 14, 2026 11:04 Inactive
@vercel vercel Bot temporarily deployed to Preview – proposals May 14, 2026 11:04 Inactive
devin-ai-integration[bot]

This comment was marked as resolved.

@vercel vercel Bot temporarily deployed to Preview – staking May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – component-library May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – api-reference May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – developer-hub May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – insights May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – entropy-explorer May 15, 2026 06:10 Inactive
@vercel vercel Bot temporarily deployed to Preview – proposals May 15, 2026 06:10 Inactive
@0xghost42
Copy link
Copy Markdown
Author

Pushed 4967b82 bumping @pythnetwork/price-pusher from 10.4.0 -> 10.5.0 per the version-bump finding.

Heads-up on coordination: #3689 (mnemonic env var) also bumps to 10.5.0, so whichever PR lands second will need a trivial rebase to 10.6.0. Both are minor backward-compatible additions, so the bump magnitude is the same regardless of order.

Re: the additional 5 findings hidden in the Devin Review console — I can't see them from the GitHub PR view, so happy to address any that surface here as separate comments.

devin-ai-integration[bot]

This comment was marked as resolved.

@vercel vercel Bot temporarily deployed to Preview – api-reference May 15, 2026 08:54 Inactive
@vercel vercel Bot temporarily deployed to Preview – component-library May 15, 2026 08:54 Inactive
@vercel vercel Bot temporarily deployed to Preview – developer-hub May 15, 2026 08:54 Inactive
@vercel vercel Bot temporarily deployed to Preview – insights May 15, 2026 08:54 Inactive
@vercel vercel Bot temporarily deployed to Preview – entropy-explorer May 15, 2026 08:54 Inactive
@vercel vercel Bot temporarily deployed to Preview – proposals May 15, 2026 08:54 Inactive
@0xghost42
Copy link
Copy Markdown
Author

Pushed 342273d adding the missing export entry to apps/price_pusher/package.json:

"./injective/endpoint-pool": {
  "default": "./dist/injective/endpoint-pool.cjs",
  "types": "./dist/injective/endpoint-pool.d.ts"
}

Confirmed the pattern matches every other source file in apps/price_pusher/src/evm/* (7 entries), injective/command, injective/injective, near/command, etc. The CJS build would have generated dist/injective/endpoint-pool.cjs but dist/injective/injective.cjs would have failed at runtime trying to require('./endpoint-pool.js') without the export gate.

Lockfile / dependencies untouched.

@vercel vercel Bot temporarily deployed to Preview – staking May 15, 2026 08:54 Inactive
0xghost42 added a commit to 0xghost42/pyth-crosschain that referenced this pull request May 21, 2026
The sample Grafana dashboard (`grafana-dashboard.sample.json`) filters
every Prometheus query by `namespace=$chain`:

  pyth_price_feeds_total{namespace="$chain"}
  pyth_price_last_published_time{namespace="$chain"}
  pyth_price_update_attempts_total{namespace="$chain", ...}
  ...

But `src/metrics.ts` only set `app="price_pusher"` as the default label
and never emitted a `namespace` label on any counter/gauge, and
`prometheus.sample.yml` did not add one via relabeling. With the
dashboard out of the box that filter matched zero series, so every
panel for a fresh deployment came up empty — flagged by Devin during
review of pyth-network#3692 and acknowledged there as pre-existing dashboard
behavior worth fixing in a separate PR. This is that PR.

Wiring:

  - new CLI option `--metrics-namespace <name>` in `src/options.ts`,
    defaulted per chain command (`evm`, `sui`, `aptos`, `solana`) so
    that single-chain deployments work without configuration
  - `PricePusherMetrics` constructor now takes a `namespace: string`
    parameter and sets it via `registry.setDefaultLabels`, so every
    existing metric series gains a `namespace` label without per-metric
    `labelNames` plumbing
  - each chain command (`evm/sui/aptos/solana/command.ts`) reads the
    new arg and passes through to the constructor

Operators running multiple deployments of the same chain (e.g. several
EVM networks against one Grafana instance) can now set
`--metrics-namespace bsc-mainnet` / `--metrics-namespace polygon-mainnet`
to disambiguate. Single-deployment setups keep working unchanged.

Bumps `@pythnetwork/price-pusher` to `10.5.0` (additive, no breaking
changes — old CLIs still work). Note: pyth-network#3689 and pyth-network#3703 also bump to
`10.5.0`, so whichever of the three lands second/third will need a
trivial rebase to `10.6.0` / `10.7.0`.

Refs pyth-network#3692.
…over

Closes pyth-network#1014.

The injective price pusher accepted a single `--grpc-endpoint`. Cosmos
nodes are occasionally flaky, so operators ended up running one pusher
process per endpoint just to get redundancy. Maintainer (jayantk) said
on the issue that cycling chain endpoints was the right shape.

- `--grpc-endpoint` is now `type: "array"`, accepting either repeated
  flag occurrences or a comma-separated list (the handler splits + trims
  each entry). A single endpoint still works exactly as before. Empty
  input is rejected up front with a clear message.
- New `src/injective/endpoint-pool.ts`:
  - `EndpointPool` — round-robin cursor over a non-empty endpoint list,
    shared across every gRPC API helper in the pusher so a bad endpoint
    affects the whole instance, not just one method.
  - `withEndpointFailover(pool, fn)` — runs `fn(currentEndpoint)`,
    rotates on failure, walks every endpoint at most once before
    re-throwing the last error.
- `InjectivePriceListener` + `InjectivePricePusher` constructors take
  `string | readonly string[]` for backward compatibility, wrap each
  gRPC call site (`ChainGrpcWasmApi.fetchSmartContractState`,
  `ChainGrpcAuthApi.fetchAccount`, `TxGrpcApi.broadcast`,
  `TxGrpcApi.simulate`) in `withEndpointFailover`.
- README: short note on the new fallback-set syntax under the Injective
  example.

- `pnpm --filter @pythnetwork/price-pusher exec test-unit` — 8 cases,
  all pass: empty rejection, default cursor, round-robin wrap,
  single-endpoint no-op, success no-rotate, single-failure rotate,
  exhaust-and-throw, cursor-position respected by `withEndpointFailover`.
- `pnpm --filter @pythnetwork/price-pusher exec tsc --noEmit` clean.
- Wired `test:unit` into the package scripts so the workspace test
  runner picks the new file up.

The original issue also mentioned the Pyth price service (Hermes) gRPC
endpoint as a possibly useful failover target. That's a different code
path (HermesClient takes a single URL today), so I kept this PR focused
on the chain-endpoint case the reporter named primarily. Happy to
follow up on the Hermes side in a separate PR if the maintainers want
it.
0xghost42 added 2 commits May 21, 2026 14:07
The exports map drives ts-duality --noEsm CJS compilation. Without an entry for src/injective/endpoint-pool.ts, dist/injective/endpoint-pool.cjs is not generated and dist/injective/injective.cjs fails at runtime when it requires ./endpoint-pool.js. Mirror the pattern used by the other injective files.
@0xghost42 0xghost42 force-pushed the feat/1014-grpc-endpoints branch from 342273d to d8aadbf Compare May 21, 2026 08:37
@vercel vercel Bot temporarily deployed to Preview – proposals May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – component-library May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – api-reference May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – entropy-explorer May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – developer-hub May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – insights May 21, 2026 08:37 Inactive
@vercel vercel Bot temporarily deployed to Preview – staking May 21, 2026 08:37 Inactive
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

"type": "module",
"types": "./dist/index.d.ts",
"version": "10.4.0"
"version": "10.5.0"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Version bump present but lockfile not updated

The package version was bumped from 10.4.0 to 10.5.0 (minor, appropriate for new functionality), but the pnpm-lock.yaml was not updated in this PR. Per REVIEW.md guidelines, lock file changes should accompany version bumps. This may need to be addressed before merge or as part of the merge process.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] price-pusher should accept an array of gRPC endpoints

1 participant