|
14 | 14 | import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; |
15 | 15 | import { DirectSecp256k1HdWallet, DirectSecp256k1Wallet, type OfflineDirectSigner } from "@cosmjs/proto-signing"; |
16 | 16 | import { GasPrice } from "@cosmjs/stargate"; |
| 17 | +import { priceUpdateCounter } from "./metrics.ts"; |
| 18 | +import { latestValue } from "./price-stream/latest-value/latest-value.ts"; |
| 19 | +import { PriceUpdateConfirmed } from "./price-update/price-update-confirmed/price-update-confirmed.ts"; |
| 20 | +import type { Logger, PriceProducerFactory, PriceUpdate, PriceUpdater, PythPriceData } from "./types.ts"; |
17 | 21 | import { |
18 | | - validateEndpointUrl, |
| 22 | + sanitizeErrorMessage, |
19 | 23 | validateAkashAddress, |
20 | 24 | validateContractAddress, |
| 25 | + validateEndpointUrl, |
21 | 26 | validateFeeAmount, |
22 | | - sanitizeErrorMessage, |
23 | 27 | validateWalletSecret, |
24 | 28 | } from "./validation.ts"; |
25 | | -import { priceUpdateCounter } from "./metrics.ts"; |
26 | | -import type { PriceUpdate, PythPriceData, PriceProducerFactory, Logger } from "./types.ts"; |
27 | | -import { latestValue } from "./price-stream/latest-value/latest-value.ts"; |
28 | 29 |
|
29 | 30 | export interface HermesConfig { |
30 | 31 | /** |
@@ -81,17 +82,6 @@ export interface HermesConfig { |
81 | 82 | // Matches pyth contract msg.rs |
82 | 83 | // ===================== |
83 | 84 |
|
84 | | -interface UpdatePriceFeedMsg { |
85 | | - update_price_feed: { |
86 | | - // VAA data from Pyth Hermes API (base64 encoded Binary) |
87 | | - // The Pyth contract will: |
88 | | - // 1. Verify VAA via Wormhole contract |
89 | | - // 2. Parse Pyth price attestation from payload |
90 | | - // 3. Relay to x/oracle module |
91 | | - vaa: string; |
92 | | - }; |
93 | | -} |
94 | | - |
95 | 85 | interface UpdateFeeMsg { |
96 | 86 | update_fee: { |
97 | 87 | new_fee: string; // Uint256 serializes as string in JSON |
@@ -176,6 +166,7 @@ export class HermesClient { |
176 | 166 | await client.initialize(); |
177 | 167 | return client; |
178 | 168 | } |
| 169 | + #priceUpdater?: PriceUpdater; |
179 | 170 |
|
180 | 171 | constructor(config: HermesConfig) { |
181 | 172 | const unsafeAllowInsecureEndpoints = config.unsafeAllowInsecureEndpoints ?? false; |
@@ -215,6 +206,7 @@ export class HermesClient { |
215 | 206 | gasPrice: GasPrice.fromString(this.#config.gasPrice), |
216 | 207 | }, |
217 | 208 | ); |
| 209 | + |
218 | 210 | this.#logger.log("Connected to chain successfully"); |
219 | 211 |
|
220 | 212 | this.#logger.log("Fetching smart contract configuration..."); |
@@ -397,43 +389,30 @@ export class HermesClient { |
397 | 389 | const startTime = performance.now(); |
398 | 390 |
|
399 | 391 | try { |
400 | | - const { priceData, vaa } = priceUpdate; |
401 | 392 | const currentPrice = await this.queryCurrentPrice(); |
402 | 393 |
|
403 | | - if (this.#canIgnorePriceUpdate(priceData, currentPrice)) { |
| 394 | + if (this.#canIgnorePriceUpdate(priceUpdate.priceData, currentPrice)) { |
404 | 395 | priceUpdateCounter.add(1, { result: "skipped" }); |
405 | 396 | return; |
406 | 397 | } |
407 | 398 |
|
408 | | - // Prepare execute message with VAA |
409 | | - // The contract will: |
410 | | - // 1. Verify VAA via Wormhole contract |
411 | | - // 2. Parse Pyth price attestation from VAA payload |
412 | | - // 3. Validate price feed ID matches expected |
413 | | - // 4. Relay validated price to x/oracle module |
414 | | - const msg: UpdatePriceFeedMsg = { |
415 | | - update_price_feed: { |
416 | | - vaa: vaa, |
417 | | - }, |
418 | | - }; |
419 | | - |
420 | 399 | const config = await this.queryConfig(); |
421 | 400 |
|
422 | 401 | // Execute update |
423 | 402 | this.#logger.log("Submitting VAA to Pyth contract..."); |
424 | 403 | this.#logger.log(` Wormhole contract: ${config.wormhole_contract}`); |
425 | | - const result = await this.#getCosmClient().execute( |
426 | | - this.#senderAddress, |
427 | | - this.#config.contractAddress, |
428 | | - msg, |
429 | | - "auto", |
430 | | - undefined, |
431 | | - [{ denom: this.#config.denom, amount: config.update_fee }], |
432 | | - ); |
| 404 | + this.#priceUpdater ??= new PriceUpdateConfirmed(this.#getCosmClient()); |
| 405 | + const result = await this.#priceUpdater.updatePrice(priceUpdate, { |
| 406 | + senderAddress: this.#senderAddress, |
| 407 | + contractAddress: this.#config.contractAddress, |
| 408 | + denom: this.#config.denom, |
| 409 | + updateFee: config.update_fee, |
| 410 | + }); |
433 | 411 |
|
| 412 | + const price = priceUpdate.priceData.price; |
434 | 413 | this.#logger.log(`Price updated successfully! TX: ${result.transactionHash}`); |
435 | 414 | this.#logger.log(` Gas used: ${result.gasUsed}`); |
436 | | - this.#logger.log(` New price: ${priceData.price.price} (expo: ${priceData.price.expo})`); |
| 415 | + this.#logger.log(` New price: ${price.price} (expo: ${price.expo})`); |
437 | 416 | priceUpdateCounter.add(1, { result: "success" }); |
438 | 417 | } catch (error) { |
439 | 418 | // SEC-04: Sanitize error messages to prevent information leakage |
@@ -574,14 +553,5 @@ export class HermesClient { |
574 | 553 |
|
575 | 554 | // Export types for external use |
576 | 555 | export type { |
577 | | - DataSourceResponse, |
578 | | - UpdatePriceFeedMsg, |
579 | | - UpdateFeeMsg, |
580 | | - TransferAdminMsg, |
581 | | - RefreshOracleParamsMsg, |
582 | | - ConfigResponse, |
583 | | - PriceResponse, |
584 | | - PriceFeedResponse, |
585 | | - PriceFeedIdResponse, |
586 | | - OracleParamsResponse, |
| 556 | + ConfigResponse, DataSourceResponse, OracleParamsResponse, PriceFeedIdResponse, PriceFeedResponse, PriceResponse, RefreshOracleParamsMsg, TransferAdminMsg, UpdateFeeMsg, |
587 | 557 | }; |
0 commit comments