Skip to content

Commit f892a5f

Browse files
committed
backup write support
1 parent a801a0d commit f892a5f

File tree

3 files changed

+60
-31
lines changed

3 files changed

+60
-31
lines changed

apps/hip-3-pusher/src/pusher/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from pydantic import BaseModel
1+
from pydantic import BaseModel, Field
2+
from typing import Optional
23

34
STALE_TIMEOUT_SECONDS = 5
45

@@ -27,6 +28,7 @@ class HermesConfig(BaseModel):
2728

2829
class HyperliquidConfig(BaseModel):
2930
hyperliquid_ws_urls: list[str]
31+
backup_push_urls: Optional[list[str]] = Field(default_factory=list)
3032
market_name: str
3133
market_symbol: str
3234
use_testnet: bool

apps/hip-3-pusher/src/pusher/kms_signer.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from eth_keys.datatypes import Signature
88
from eth_utils import keccak, to_hex
99
from hyperliquid.exchange import Exchange
10-
from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
1110
from hyperliquid.utils.signing import get_timestamp_ms, action_hash, construct_phantom_agent, l1_payload
1211
from loguru import logger
1312
from pathlib import Path
@@ -27,10 +26,9 @@ def _init_client():
2726

2827

2928
class KMSSigner:
30-
def __init__(self, config: Config):
29+
def __init__(self, config: Config, publisher_exchanges: list[Exchange]):
3130
self.use_testnet = config.hyperliquid.use_testnet
32-
url = TESTNET_API_URL if self.use_testnet else MAINNET_API_URL
33-
self.oracle_publisher_exchange: Exchange = Exchange(wallet=None, base_url=url)
31+
self.publisher_exchanges = publisher_exchanges
3432

3533
# AWS client and public key load
3634
self.client = _init_client()
@@ -79,11 +77,20 @@ def set_oracle(self, dex, oracle_pxs, all_mark_pxs, external_perp_pxs):
7977
nonce=timestamp,
8078
is_mainnet= self.use_testnet,
8179
)
82-
return self.oracle_publisher_exchange._post_action(
83-
action=action,
84-
signature=signature,
85-
nonce=timestamp,
86-
)
80+
return self._send_update(action, signature, timestamp)
81+
82+
def _send_update(self, action, signature, timestamp):
83+
for exchange in self.publisher_exchanges:
84+
try:
85+
return exchange._post_action(
86+
action=action,
87+
signature=signature,
88+
nonce=timestamp,
89+
)
90+
except Exception as e:
91+
logger.exception("perp_deploy_set_oracle exception for endpoint: {} error: {}", exchange.base_url, e)
92+
93+
return None
8794

8895
def sign_l1_action(self, action, nonce, is_mainnet):
8996
hash = action_hash(action, vault_address=None, nonce=nonce, expires_after=None)

apps/hip-3-pusher/src/pusher/publisher.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,21 @@ class Publisher:
2121
"""
2222
def __init__(self, config: Config, price_state: PriceState, metrics: Metrics):
2323
self.publish_interval = float(config.hyperliquid.publish_interval)
24-
self.kms_signer = None
25-
self.enable_kms = False
2624
self.use_testnet = config.hyperliquid.use_testnet
25+
self.push_urls = [TESTNET_API_URL if self.use_testnet else MAINNET_API_URL] + config.hyperliquid.backup_push_urls
2726

28-
if config.kms.enable_kms:
29-
self.enable_kms = True
30-
oracle_account = None
31-
self.kms_signer = KMSSigner(config)
32-
else:
33-
oracle_pusher_key_path = config.hyperliquid.oracle_pusher_key_path
34-
oracle_pusher_key = Path(oracle_pusher_key_path).read_text().strip()
27+
self.kms_signer = None
28+
self.enable_kms = False
29+
oracle_account = None
30+
if not config.kms.enable_kms:
31+
oracle_pusher_key = Path(config.hyperliquid.oracle_pusher_key_path).read_text().strip()
3532
oracle_account: LocalAccount = Account.from_key(oracle_pusher_key)
3633
logger.info("oracle pusher local pubkey: {}", oracle_account.address)
34+
self.publisher_exchanges = [Exchange(wallet=oracle_account, base_url=url) for url in self.push_urls]
35+
if config.kms.enable_kms:
36+
self.enable_kms = True
37+
self.kms_signer = KMSSigner(config, self.publisher_exchanges)
3738

38-
url = TESTNET_API_URL if self.use_testnet else MAINNET_API_URL
39-
self.oracle_publisher_exchange: Exchange = Exchange(wallet=oracle_account, base_url=url)
4039
self.market_name = config.hyperliquid.market_name
4140
self.market_symbol = config.hyperliquid.market_symbol
4241
self.enable_publish = config.hyperliquid.enable_publish
@@ -72,6 +71,7 @@ def publish(self):
7271
# TODO: "Each update can change oraclePx and markPx by at most 1%."
7372
# TODO: "The markPx cannot be updated such that open interest would be 10x the open interest cap."
7473

74+
push_response = None
7575
if self.enable_publish:
7676
if self.enable_kms:
7777
push_response = self.kms_signer.set_oracle(
@@ -81,18 +81,38 @@ def publish(self):
8181
external_perp_pxs=external_perp_pxs,
8282
)
8383
else:
84-
push_response = self.oracle_publisher_exchange.perp_deploy_set_oracle(
85-
dex=self.market_name,
84+
push_response = self._send_update(
8685
oracle_pxs=oracle_pxs,
8786
all_mark_pxs=mark_pxs,
8887
external_perp_pxs=external_perp_pxs,
8988
)
9089

91-
# TODO: Look at specific error responses and log/alert accordingly
92-
logger.debug("publish: push response: {} {}", push_response, type(push_response))
93-
status = push_response.get("status", "")
94-
if status == "ok":
95-
self.metrics.successful_push_counter.add(1, self.metrics_labels)
96-
elif status == "err":
97-
self.metrics.failed_push_counter.add(1, self.metrics_labels)
98-
logger.error("publish: publish error: {}", push_response)
90+
self._handle_response(push_response)
91+
92+
def _send_update(self, oracle_pxs, all_mark_pxs, external_perp_pxs):
93+
for exchange in self.publisher_exchanges:
94+
try:
95+
return exchange.perp_deploy_set_oracle(
96+
dex=self.market_name,
97+
oracle_pxs=oracle_pxs,
98+
all_mark_pxs=all_mark_pxs,
99+
external_perp_pxs=external_perp_pxs,
100+
)
101+
except Exception as e:
102+
logger.exception("perp_deploy_set_oracle exception for endpoint: {} error: {}", exchange.base_url, e)
103+
104+
return None
105+
106+
def _handle_response(self, response):
107+
if response is None:
108+
logger.error("Push API call failed")
109+
self.metrics.failed_push_counter.add(1, self.metrics_labels)
110+
return
111+
112+
logger.debug("publish: push response: {} {}", response, type(response))
113+
status = response.get("status")
114+
if status == "ok":
115+
self.metrics.successful_push_counter.add(1, self.metrics_labels)
116+
elif status == "err":
117+
self.metrics.failed_push_counter.add(1, self.metrics_labels)
118+
logger.error("publish: publish error: {}", response)

0 commit comments

Comments
 (0)