Skip to content

Commit 740c5e1

Browse files
authored
fix(hip-3-pusher): Multisig update support (only 1/N, no KMS) (#3147)
* fix(hip-3-pusher): Multisig update support (only 1/N, no KMS) * fix market name * newline
1 parent cb982f0 commit 740c5e1

File tree

5 files changed

+74
-8
lines changed

5 files changed

+74
-8
lines changed

apps/hip-3-pusher/config/config.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ publish_interval = 3.0
1111
publish_timeout = 5.0
1212
enable_publish = false
1313

14+
[multisig]
15+
enable_multisig = false
16+
multisig_address = "0x0000000000000000000000000000000000000005"
17+
1418
[kms]
1519
enable_kms = false
1620
aws_kms_key_id_path = "/path/to/aws_kms_key_id.txt"

apps/hip-3-pusher/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "hip-3-pusher"
3-
version = "0.1.6"
3+
version = "0.1.7"
44
description = "Hyperliquid HIP-3 market oracle pusher"
55
readme = "README.md"
66
requires-python = "==3.13.*"

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ class KMSConfig(BaseModel):
1010
aws_kms_key_id_path: Optional[FilePath] = None
1111

1212

13+
class MultisigConfig(BaseModel):
14+
enable_multisig: bool
15+
multisig_address: Optional[str] = None
16+
17+
1318
class LazerConfig(BaseModel):
1419
lazer_urls: list[str]
1520
lazer_api_key: str
@@ -52,3 +57,4 @@ class Config(BaseModel):
5257
kms: KMSConfig
5358
lazer: LazerConfig
5459
hermes: HermesConfig
60+
multisig: MultisigConfig

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

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from eth_account import Account
88
from eth_account.signers.local import LocalAccount
99
from hyperliquid.exchange import Exchange
10+
from hyperliquid.utils.signing import get_timestamp_ms, sign_multi_sig_l1_action_payload
1011

1112
from pusher.config import Config
1213
from pusher.exception import PushError
@@ -29,19 +30,28 @@ def __init__(self, config: Config, price_state: PriceState, metrics: Metrics):
2930

3031
self.kms_signer = None
3132
self.enable_kms = False
32-
oracle_account = None
33+
self.oracle_account = None
3334
if not config.kms.enable_kms:
3435
oracle_pusher_key = Path(config.hyperliquid.oracle_pusher_key_path).read_text().strip()
35-
oracle_account: LocalAccount = Account.from_key(oracle_pusher_key)
36-
logger.info("oracle pusher local pubkey: {}", oracle_account.address)
37-
self.publisher_exchanges = [Exchange(wallet=oracle_account,
36+
self.oracle_account: LocalAccount = Account.from_key(oracle_pusher_key)
37+
logger.info("oracle pusher local pubkey: {}", self.oracle_account.address)
38+
self.publisher_exchanges = [Exchange(wallet=self.oracle_account,
3839
base_url=url,
3940
timeout=config.hyperliquid.publish_timeout)
4041
for url in self.push_urls]
4142
if config.kms.enable_kms:
43+
# TODO: Add KMS/multisig support
44+
if config.multisig.enable_multisig:
45+
raise Exception("KMS/multisig not yet supported")
46+
4247
self.enable_kms = True
4348
self.kms_signer = KMSSigner(config, self.publisher_exchanges)
4449

50+
if config.multisig.enable_multisig:
51+
if not config.multisig.multisig_address:
52+
raise Exception("Multisig enabled but missing multisig address")
53+
self.multisig_address = config.multisig.multisig_address
54+
4555
self.market_name = config.hyperliquid.market_name
4656
self.market_symbol = config.hyperliquid.market_symbol
4757
self.enable_publish = config.hyperliquid.enable_publish
@@ -68,12 +78,12 @@ def publish(self):
6878
return
6979
else:
7080
logger.debug("Current oracle price: {}", oracle_px)
71-
oracle_pxs[self.market_symbol] = oracle_px
81+
oracle_pxs[f"{self.market_name}:{self.market_symbol}"] = oracle_px
7282

7383
mark_pxs = []
7484
external_perp_pxs = {}
7585
if self.price_state.hl_mark_price:
76-
external_perp_pxs[self.market_symbol] = self.price_state.hl_mark_price.price
86+
external_perp_pxs[f"{self.market_name}:{self.market_symbol}"] = self.price_state.hl_mark_price.price
7787

7888
# TODO: "Each update can change oraclePx and markPx by at most 1%."
7989
# TODO: "The markPx cannot be updated such that open interest would be 10x the open interest cap."
@@ -87,6 +97,12 @@ def publish(self):
8797
all_mark_pxs=mark_pxs,
8898
external_perp_pxs=external_perp_pxs,
8999
)
100+
elif self.multisig_address:
101+
push_response = self._send_multisig_update(
102+
oracle_pxs=oracle_pxs,
103+
all_mark_pxs=mark_pxs,
104+
external_perp_pxs=external_perp_pxs,
105+
)
90106
else:
91107
push_response = self._send_update(
92108
oracle_pxs=oracle_pxs,
@@ -133,3 +149,43 @@ def _record_push_interval_metric(self):
133149
self.metrics.push_interval_histogram.record(push_interval, self.metrics_labels)
134150
self.last_push_time = now
135151
logger.debug("Push interval: {}", push_interval)
152+
153+
def _send_multisig_update(self, oracle_pxs, all_mark_pxs, external_perp_pxs):
154+
for exchange in self.publisher_exchanges:
155+
try:
156+
return self._send_single_multisig_update(
157+
exchange=exchange,
158+
oracle_pxs=oracle_pxs,
159+
all_mark_pxs=all_mark_pxs,
160+
external_perp_pxs=external_perp_pxs,
161+
)
162+
except Exception as e:
163+
logger.exception("_send_single_multisig_update exception for endpoint: {} error: {}", exchange.base_url, repr(e))
164+
165+
raise PushError("all push endpoints failed for multisig")
166+
167+
def _send_single_multisig_update(self, exchange, oracle_pxs, all_mark_pxs, external_perp_pxs):
168+
timestamp = get_timestamp_ms()
169+
oracle_pxs_wire = sorted(list(oracle_pxs.items()))
170+
mark_pxs_wire = [sorted(list(mark_pxs.items())) for mark_pxs in all_mark_pxs]
171+
external_perp_pxs_wire = sorted(list(external_perp_pxs.items()))
172+
action = {
173+
"type": "perpDeploy",
174+
"setOracle": {
175+
"dex": self.market_name,
176+
"oraclePxs": oracle_pxs_wire,
177+
"markPxs": mark_pxs_wire,
178+
"externalPerpPxs": external_perp_pxs_wire,
179+
},
180+
}
181+
signatures = [sign_multi_sig_l1_action_payload(
182+
wallet=self.oracle_account,
183+
action=action,
184+
is_mainnet=not self.use_testnet,
185+
vault_address=None,
186+
timestamp=timestamp,
187+
expires_after=None,
188+
payload_multi_sig_user=self.multisig_address,
189+
outer_signer=self.oracle_account.address,
190+
)]
191+
return exchange.multi_sig(self.multisig_address, action, signatures, timestamp)

apps/hip-3-pusher/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)