77from  eth_account  import  Account 
88from  eth_account .signers .local  import  LocalAccount 
99from  hyperliquid .exchange  import  Exchange 
10+ from  hyperliquid .utils .signing  import  get_timestamp_ms , sign_multi_sig_l1_action_payload 
1011
1112from  pusher .config  import  Config 
1213from  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 )
0 commit comments