ccxt_exchanges/hyperliquid/
rest.rs

1//! HyperLiquid REST API implementation.
2//!
3//! Implements all REST API endpoint operations for the HyperLiquid exchange.
4
5use super::{HyperLiquid, error, parser};
6use ccxt_core::{
7    Error, ParseError, Result,
8    types::{
9        Amount, Balance, Market, Order, OrderBook, OrderSide, OrderType, Price, Ticker, Trade,
10    },
11};
12use rust_decimal::Decimal;
13use serde_json::{Map, Value};
14use std::{collections::HashMap, sync::Arc};
15use tracing::{debug, info, warn};
16
17impl HyperLiquid {
18    // ============================================================================
19    // Helper Methods
20    // ============================================================================
21
22    /// Make a public info API request.
23    pub(crate) async fn info_request(&self, request_type: &str, payload: Value) -> Result<Value> {
24        let urls = self.urls();
25        let url = format!("{}/info", urls.rest);
26
27        // Build the request body by merging type with payload
28        let body = if let Value::Object(map) = payload {
29            let mut obj = serde_json::Map::new();
30            obj.insert("type".to_string(), Value::String(request_type.to_string()));
31            for (k, v) in map {
32                obj.insert(k, v);
33            }
34            Value::Object(obj)
35        } else {
36            let mut map = serde_json::Map::new();
37            map.insert(
38                "type".to_string(),
39                serde_json::Value::String(request_type.to_string()),
40            );
41            serde_json::Value::Object(map)
42        };
43
44        debug!("HyperLiquid info request: {} {:?}", request_type, body);
45
46        let response = self.base().http_client.post(&url, None, Some(body)).await?;
47
48        if error::is_error_response(&response) {
49            return Err(error::parse_error(&response));
50        }
51
52        Ok(response)
53    }
54
55    /// Make an exchange action request (requires authentication).
56    ///
57    /// # Deprecated
58    ///
59    /// This method is deprecated. Use `signed_action()` builder instead:
60    ///
61    /// ```no_run
62    /// # use ccxt_exchanges::hyperliquid::HyperLiquid;
63    /// # use serde_json::json;
64    /// # async fn example() -> ccxt_core::Result<()> {
65    /// let exchange = HyperLiquid::builder()
66    ///     .private_key("0x...")
67    ///     .testnet(true)
68    ///     .build()?;
69    ///
70    /// let action = json!({"type": "order", "orders": [], "grouping": "na"});
71    /// let response = exchange.signed_action(action).execute().await?;
72    /// # Ok(())
73    /// # }
74    /// ```
75    #[deprecated(since = "0.2.0", note = "Use signed_action() builder instead")]
76    pub async fn exchange_request(&self, action: Value, nonce: u64) -> Result<Value> {
77        self.signed_action(action).nonce(nonce).execute().await
78    }
79
80    /// Get current timestamp as nonce.
81    ///
82    /// # Deprecated
83    ///
84    /// This method is deprecated. The `signed_action()` builder automatically
85    /// generates the nonce. Use `signed_action().nonce(value)` if you need
86    /// to override the auto-generated nonce.
87    #[deprecated(
88        since = "0.2.0",
89        note = "Use signed_action() builder which auto-generates nonce"
90    )]
91    pub fn get_nonce(&self) -> u64 {
92        chrono::Utc::now().timestamp_millis() as u64
93    }
94
95    // ============================================================================
96    // Public API Methods - Market Data
97    // ============================================================================
98
99    /// Fetch all trading markets.
100    pub async fn fetch_markets(&self) -> Result<Arc<HashMap<String, Arc<Market>>>> {
101        let response = self
102            .info_request("meta", serde_json::Value::Object(serde_json::Map::new()))
103            .await?;
104
105        let universe = response["universe"]
106            .as_array()
107            .ok_or_else(|| Error::from(ParseError::missing_field("universe")))?;
108
109        let mut markets = Vec::new();
110        for (index, asset) in universe.iter().enumerate() {
111            match parser::parse_market(asset, index) {
112                Ok(market) => markets.push(market),
113                Err(e) => {
114                    warn!(error = %e, "Failed to parse market");
115                }
116            }
117        }
118
119        // Cache the markets and preserve ownership for the caller
120        let result = self.base().set_markets(markets, None).await?;
121
122        info!("Loaded {} markets for HyperLiquid", result.len());
123        Ok(result)
124    }
125
126    /// Load and cache market data.
127    pub async fn load_markets(&self, reload: bool) -> Result<Arc<HashMap<String, Arc<Market>>>> {
128        // Acquire the loading lock to serialize concurrent load_markets calls
129        // This prevents multiple tasks from making duplicate API calls
130        let _loading_guard = self.base().market_loading_lock.lock().await;
131
132        // Check cache status while holding the lock
133        {
134            let cache = self.base().market_cache.read().await;
135            if cache.is_loaded() && !reload {
136                debug!(
137                    "Returning cached markets for HyperLiquid ({} markets)",
138                    cache.market_count()
139                );
140                return Ok(cache.markets());
141            }
142        }
143
144        info!("Loading markets for HyperLiquid (reload: {})", reload);
145        let _markets = self.fetch_markets().await?;
146
147        let cache = self.base().market_cache.read().await;
148        Ok(cache.markets())
149    }
150
151    /// Fetch ticker for a single trading pair.
152    pub async fn fetch_ticker(&self, symbol: &str) -> Result<Ticker> {
153        let market = self.base().market(symbol).await?;
154        let response = self
155            .info_request("allMids", serde_json::Value::Object(serde_json::Map::new()))
156            .await?;
157
158        // Response is a map of asset name to mid price
159        let mid_price = response[&market.base]
160            .as_str()
161            .and_then(|s| s.parse::<Decimal>().ok())
162            .ok_or_else(|| Error::bad_symbol(format!("No ticker data for {}", symbol)))?;
163
164        parser::parse_ticker(symbol, mid_price, Some(&market))
165    }
166
167    /// Fetch tickers for multiple trading pairs.
168    pub async fn fetch_tickers(&self, symbols: Option<Vec<String>>) -> Result<Vec<Ticker>> {
169        let response = self
170            .info_request("allMids", serde_json::Value::Object(serde_json::Map::new()))
171            .await?;
172
173        let cache = self.base().market_cache.read().await;
174        if !cache.is_loaded() {
175            drop(cache);
176            return Err(Error::exchange(
177                "-1",
178                "Markets not loaded. Call load_markets() first.",
179            ));
180        }
181        drop(cache);
182
183        let mut tickers = Vec::new();
184
185        if let Some(obj) = response.as_object() {
186            for (asset, price) in obj {
187                if let Some(mid_price) = price.as_str().and_then(|s| s.parse::<Decimal>().ok()) {
188                    let symbol = format!("{}/USDC:USDC", asset);
189
190                    // Filter by requested symbols if provided
191                    if let Some(ref syms) = symbols {
192                        if !syms.contains(&symbol) {
193                            continue;
194                        }
195                    }
196
197                    if let Ok(ticker) = parser::parse_ticker(&symbol, mid_price, None) {
198                        tickers.push(ticker);
199                    }
200                }
201            }
202        }
203
204        Ok(tickers)
205    }
206
207    /// Fetch order book for a trading pair.
208    pub async fn fetch_order_book(&self, symbol: &str, _limit: Option<u32>) -> Result<OrderBook> {
209        let market = self.base().market(symbol).await?;
210
211        let response = self
212            .info_request("l2Book", {
213                let mut map = serde_json::Map::new();
214                map.insert(
215                    "coin".to_string(),
216                    serde_json::Value::String(market.base.clone()),
217                );
218                serde_json::Value::Object(map)
219            })
220            .await?;
221
222        parser::parse_orderbook(&response, symbol.to_string())
223    }
224
225    /// Fetch recent public trades.
226    pub async fn fetch_trades(&self, symbol: &str, limit: Option<u32>) -> Result<Vec<Trade>> {
227        let market = self.base().market(symbol).await?;
228        let limit = limit.unwrap_or(100).min(1000);
229
230        let response = self
231            .info_request("recentTrades", {
232                let mut map = serde_json::Map::new();
233                map.insert(
234                    "coin".to_string(),
235                    serde_json::Value::String(market.base.clone()),
236                );
237                map.insert("n".to_string(), serde_json::Value::Number(limit.into()));
238                serde_json::Value::Object(map)
239            })
240            .await?;
241
242        let trades_array = response
243            .as_array()
244            .ok_or_else(|| Error::from(ParseError::invalid_format("data", "Expected array")))?;
245
246        let mut trades = Vec::new();
247        for trade_data in trades_array {
248            match parser::parse_trade(trade_data, Some(&market)) {
249                Ok(trade) => trades.push(trade),
250                Err(e) => {
251                    warn!(error = %e, "Failed to parse trade");
252                }
253            }
254        }
255
256        Ok(trades)
257    }
258
259    /// Fetch OHLCV (candlestick) data.
260    pub async fn fetch_ohlcv(
261        &self,
262        symbol: &str,
263        timeframe: &str,
264        since: Option<i64>,
265        limit: Option<u32>,
266    ) -> Result<Vec<ccxt_core::types::Ohlcv>> {
267        let market = self.base().market(symbol).await?;
268        let limit = limit.unwrap_or(500).min(5000) as i64;
269
270        // Convert timeframe to HyperLiquid interval format
271        let interval = match timeframe {
272            "1m" => "1m",
273            "5m" => "5m",
274            "15m" => "15m",
275            "30m" => "30m",
276            "4h" => "4h",
277            "1d" => "1d",
278            "1w" => "1w",
279            _ => "1h",
280        };
281
282        let now = chrono::Utc::now().timestamp_millis() as u64;
283        let start_time = since.map_or(now - limit as u64 * 3600000, |s| s as u64); // Default to limit hours ago
284        let end_time = now;
285
286        let response = self
287            .info_request("candleSnapshot", {
288                let mut map = serde_json::Map::new();
289                map.insert(
290                    "coin".to_string(),
291                    serde_json::Value::String(market.base.clone()),
292                );
293                map.insert(
294                    "interval".to_string(),
295                    serde_json::Value::String(interval.to_string()),
296                );
297                map.insert(
298                    "startTime".to_string(),
299                    serde_json::Value::Number(start_time.into()),
300                );
301                map.insert(
302                    "endTime".to_string(),
303                    serde_json::Value::Number(end_time.into()),
304                );
305                serde_json::Value::Object(map)
306            })
307            .await?;
308
309        let candles_array = response
310            .as_array()
311            .ok_or_else(|| Error::from(ParseError::invalid_format("data", "Expected array")))?;
312
313        let mut ohlcv_list = Vec::new();
314        for candle_data in candles_array {
315            match parser::parse_ohlcv(candle_data) {
316                Ok(ohlcv) => ohlcv_list.push(ohlcv),
317                Err(e) => {
318                    warn!(error = %e, "Failed to parse OHLCV");
319                }
320            }
321        }
322
323        Ok(ohlcv_list)
324    }
325
326    /// Fetch current funding rate for a symbol.
327    pub async fn fetch_funding_rate(&self, symbol: &str) -> Result<ccxt_core::types::FundingRate> {
328        let market = self.base().market(symbol).await?;
329
330        let response = self
331            .info_request("meta", serde_json::Value::Object(serde_json::Map::new()))
332            .await?;
333
334        // Find the asset in the universe
335        let universe = response["universe"]
336            .as_array()
337            .ok_or_else(|| Error::from(ParseError::missing_field("universe")))?;
338
339        let asset_index: usize = market.id.parse().unwrap_or(0);
340        let asset_data = universe
341            .get(asset_index)
342            .ok_or_else(|| Error::bad_symbol(format!("Asset not found: {}", symbol)))?;
343
344        let funding_rate = asset_data["funding"]
345            .as_str()
346            .and_then(|s| s.parse::<f64>().ok())
347            .unwrap_or(0.0);
348
349        let timestamp = chrono::Utc::now().timestamp_millis();
350
351        Ok(ccxt_core::types::FundingRate {
352            info: serde_json::json!({}),
353            symbol: symbol.to_string(),
354            mark_price: None,
355            index_price: None,
356            interest_rate: None,
357            estimated_settle_price: None,
358            funding_rate: Some(funding_rate),
359            funding_timestamp: None,
360            funding_datetime: None,
361            previous_funding_rate: None,
362            previous_funding_timestamp: None,
363            previous_funding_datetime: None,
364            timestamp: Some(timestamp),
365            datetime: parser::timestamp_to_datetime(timestamp),
366        })
367    }
368
369    // ============================================================================
370    // Private API Methods - Account
371    // ============================================================================
372
373    /// Fetch account balance.
374    pub async fn fetch_balance(&self) -> Result<Balance> {
375        let address = self
376            .wallet_address()
377            .ok_or_else(|| Error::authentication("Private key required to fetch balance"))?;
378
379        let response = self
380            .info_request("clearinghouseState", {
381                let mut map = Map::new();
382                map.insert("user".to_string(), Value::String(address.to_string()));
383                Value::Object(map)
384            })
385            .await?;
386
387        parser::parse_balance(&response)
388    }
389
390    /// Fetch open positions.
391    pub async fn fetch_positions(
392        &self,
393        symbols: Option<Vec<String>>,
394    ) -> Result<Vec<ccxt_core::types::Position>> {
395        let address = self
396            .wallet_address()
397            .ok_or_else(|| Error::authentication("Private key required to fetch positions"))?;
398
399        let response = self
400            .info_request("clearinghouseState", {
401                let mut map = Map::new();
402                map.insert("user".to_string(), Value::String(address.to_string()));
403                Value::Object(map)
404            })
405            .await?;
406
407        let asset_positions = response["assetPositions"]
408            .as_array()
409            .ok_or_else(|| Error::from(ParseError::missing_field("assetPositions")))?;
410
411        let mut positions = Vec::new();
412        for pos_data in asset_positions {
413            let position = pos_data.get("position").unwrap_or(pos_data);
414
415            let coin = position["coin"].as_str().unwrap_or("");
416            let symbol = format!("{}/USDC:USDC", coin);
417
418            // Filter by symbols if provided
419            if let Some(ref syms) = symbols {
420                if !syms.contains(&symbol) {
421                    continue;
422                }
423            }
424
425            let szi = position["szi"]
426                .as_str()
427                .and_then(|s| s.parse::<f64>().ok())
428                .unwrap_or(0.0);
429
430            // Skip zero positions
431            if szi.abs() < 1e-10 {
432                continue;
433            }
434
435            let entry_px = position["entryPx"]
436                .as_str()
437                .and_then(|s| s.parse::<f64>().ok());
438            let liquidation_px = position["liquidationPx"]
439                .as_str()
440                .and_then(|s| s.parse::<f64>().ok());
441            let unrealized_pnl = position["unrealizedPnl"]
442                .as_str()
443                .and_then(|s| s.parse::<f64>().ok());
444            let margin_used = position["marginUsed"]
445                .as_str()
446                .and_then(|s| s.parse::<f64>().ok());
447
448            let leverage_info = position.get("leverage");
449            let leverage = leverage_info
450                .and_then(|l| l["value"].as_str())
451                .and_then(|s| s.parse::<f64>().ok());
452            let margin_mode = leverage_info
453                .and_then(|l| l["type"].as_str())
454                .map(|t| if t == "cross" { "cross" } else { "isolated" }.to_string());
455
456            let side = if szi > 0.0 { "long" } else { "short" };
457
458            positions.push(ccxt_core::types::Position {
459                info: pos_data.clone(),
460                id: None,
461                symbol,
462                side: Some(side.to_string()),
463                position_side: None,
464                dual_side_position: None,
465                contracts: Some(szi.abs()),
466                contract_size: Some(1.0),
467                entry_price: entry_px,
468                mark_price: None,
469                notional: None,
470                leverage,
471                collateral: margin_used,
472                initial_margin: margin_used,
473                initial_margin_percentage: None,
474                maintenance_margin: None,
475                maintenance_margin_percentage: None,
476                unrealized_pnl,
477                realized_pnl: None,
478                liquidation_price: liquidation_px,
479                margin_ratio: None,
480                margin_mode,
481                hedged: None,
482                percentage: None,
483                timestamp: Some(chrono::Utc::now().timestamp_millis()),
484                datetime: None,
485            });
486        }
487
488        Ok(positions)
489    }
490
491    /// Fetch open orders.
492    pub async fn fetch_open_orders(
493        &self,
494        symbol: Option<&str>,
495        _since: Option<i64>,
496        _limit: Option<u32>,
497    ) -> Result<Vec<Order>> {
498        let address = self
499            .wallet_address()
500            .ok_or_else(|| Error::authentication("Private key required to fetch orders"))?;
501
502        let response = self
503            .info_request("openOrders", {
504                let mut map = Map::new();
505                map.insert("user".to_string(), Value::String(address.to_string()));
506                Value::Object(map)
507            })
508            .await?;
509
510        let orders_array = response
511            .as_array()
512            .ok_or_else(|| Error::from(ParseError::invalid_format("data", "Expected array")))?;
513
514        let mut orders = Vec::new();
515        for order_data in orders_array {
516            match parser::parse_order(order_data, None) {
517                Ok(order) => {
518                    // Filter by symbol if provided
519                    if let Some(sym) = symbol {
520                        if order.symbol != sym {
521                            continue;
522                        }
523                    }
524                    orders.push(order);
525                }
526                Err(e) => {
527                    warn!(error = %e, "Failed to parse order");
528                }
529            }
530        }
531
532        Ok(orders)
533    }
534
535    // ============================================================================
536    // Private API Methods - Order Management
537    // ============================================================================
538
539    /// Create a new order.
540    ///
541    /// # Arguments
542    ///
543    /// * `symbol` - Trading pair symbol.
544    /// * `order_type` - Order type (Market, Limit, etc.).
545    /// * `side` - Order side (Buy or Sell).
546    /// * `amount` - Order quantity as [`Amount`] type.
547    /// * `price` - Optional price as [`Price`] type (required for limit orders).
548    ///
549    /// # Returns
550    ///
551    /// Returns the created [`Order`] structure with order details.
552    ///
553    /// # Errors
554    ///
555    /// Returns an error if authentication fails, market is not found, or the API request fails.
556    pub async fn create_order(
557        &self,
558        symbol: &str,
559        order_type: OrderType,
560        side: OrderSide,
561        amount: Amount,
562        price: Option<Price>,
563    ) -> Result<Order> {
564        let market = self.base().market(symbol).await?;
565        let asset_index: u32 = market.id.parse().unwrap_or(0);
566
567        let is_buy = matches!(side, OrderSide::Buy);
568        let limit_px = price.map_or_else(|| "0".to_string(), |p| p.to_string());
569
570        let order_wire = if order_type == OrderType::Market {
571            let mut limit_map = Map::new();
572            limit_map.insert("tif".to_string(), Value::String("Ioc".to_string()));
573            let mut map = Map::new();
574            map.insert("limit".to_string(), Value::Object(limit_map));
575            Value::Object(map)
576        } else {
577            let mut limit_map = Map::new();
578            limit_map.insert("tif".to_string(), Value::String("Gtc".to_string()));
579            let mut map = Map::new();
580            map.insert("limit".to_string(), Value::Object(limit_map));
581            Value::Object(map)
582        };
583
584        let action = {
585            let mut order_map = Map::new();
586            order_map.insert("a".to_string(), Value::Number(asset_index.into()));
587            order_map.insert("b".to_string(), Value::Bool(is_buy));
588            order_map.insert("p".to_string(), Value::String(limit_px));
589            order_map.insert("s".to_string(), Value::String(amount.to_string()));
590            order_map.insert("r".to_string(), Value::Bool(false));
591            order_map.insert("t".to_string(), order_wire);
592
593            let mut map = Map::new();
594            map.insert("type".to_string(), Value::String("order".to_string()));
595            map.insert(
596                "orders".to_string(),
597                Value::Array(vec![Value::Object(order_map)]),
598            );
599            map.insert("grouping".to_string(), Value::String("na".to_string()));
600            Value::Object(map)
601        };
602
603        let response = self.signed_action(action).execute().await?;
604
605        // Parse response
606        if let Some(statuses) = response["response"]["data"]["statuses"].as_array() {
607            if let Some(status) = statuses.first() {
608                if let Some(resting) = status.get("resting") {
609                    return parser::parse_order(resting, Some(&market));
610                }
611                if let Some(filled) = status.get("filled") {
612                    return parser::parse_order(filled, Some(&market));
613                }
614            }
615        }
616
617        Err(Error::exchange("-1", "Failed to parse order response"))
618    }
619
620    /// Cancel an order.
621    pub async fn cancel_order(&self, id: &str, symbol: &str) -> Result<Order> {
622        let market = self.base().market(symbol).await?;
623        let asset_index: u32 = market.id.parse().unwrap_or(0);
624        let order_id: u64 = id
625            .parse()
626            .map_err(|_| Error::invalid_request("Invalid order ID format"))?;
627
628        let action = {
629            let mut cancel_map = Map::new();
630            cancel_map.insert("a".to_string(), asset_index.into());
631            cancel_map.insert("o".to_string(), order_id.into());
632
633            let mut map = Map::new();
634            map.insert("type".to_string(), Value::String("cancel".to_string()));
635            map.insert(
636                "cancels".to_string(),
637                Value::Array(vec![Value::Object(cancel_map)]),
638            );
639            Value::Object(map)
640        };
641
642        let _response = self.signed_action(action).execute().await?;
643
644        // Return a minimal order object indicating cancellation
645        Ok(Order::new(
646            id.to_string(),
647            symbol.to_string(),
648            OrderType::Limit,
649            OrderSide::Buy, // Side is unknown for cancel response
650            Decimal::ZERO,
651            None,
652            ccxt_core::types::OrderStatus::Cancelled,
653        ))
654    }
655
656    /// Set leverage for a symbol.
657    pub async fn set_leverage(&self, symbol: &str, leverage: u32, is_cross: bool) -> Result<()> {
658        let market = self.base().market(symbol).await?;
659        let asset_index: u32 = market.id.parse().unwrap_or(0);
660
661        let leverage_type = if is_cross { "cross" } else { "isolated" };
662
663        let action = {
664            let mut map = Map::new();
665            map.insert(
666                "type".to_string(),
667                Value::String("updateLeverage".to_string()),
668            );
669            map.insert("asset".to_string(), asset_index.into());
670            map.insert("isCross".to_string(), is_cross.into());
671            map.insert("leverage".to_string(), leverage.into());
672            Value::Object(map)
673        };
674
675        let response = self.signed_action(action).execute().await?;
676
677        // Check for success
678        if error::is_error_response(&response) {
679            return Err(error::parse_error(&response));
680        }
681
682        info!(
683            "Set leverage for {} to {}x ({})",
684            symbol, leverage, leverage_type
685        );
686
687        Ok(())
688    }
689
690    /// Cancel all orders for a symbol.
691    pub async fn cancel_all_orders(&self, symbol: Option<&str>) -> Result<Vec<Order>> {
692        let _address = self
693            .wallet_address()
694            .ok_or_else(|| Error::authentication("Private key required to cancel orders"))?;
695
696        // First fetch open orders
697        let open_orders = self.fetch_open_orders(symbol, None, None).await?;
698
699        if open_orders.is_empty() {
700            return Ok(Vec::new());
701        }
702
703        // Build cancel requests for all orders
704        let mut cancels = Vec::new();
705        for order in &open_orders {
706            let market = self.base().market(&order.symbol).await?;
707            let asset_index: u32 = market.id.parse().unwrap_or(0);
708            let order_id: u64 = order.id.parse().unwrap_or(0);
709
710            cancels.push(
711                {
712                    let mut map = Map::new();
713                    map.insert("a".to_string(), asset_index.into());
714                    map.insert("o".to_string(), order_id.into());
715                    Ok(Value::Object(map))
716                }
717                .map_err(|e: serde_json::Error| Error::from(ParseError::from(e)))?,
718            );
719        }
720
721        let action = {
722            let mut map = Map::new();
723            map.insert("type".to_string(), Value::String("cancel".to_string()));
724            map.insert("cancels".to_string(), Value::Array(cancels));
725            Value::Object(map)
726        };
727
728        let _response = self.signed_action(action).execute().await?;
729
730        // Return the orders that were canceled
731        let canceled_orders: Vec<Order> = open_orders
732            .into_iter()
733            .map(|mut o| {
734                o.status = ccxt_core::types::OrderStatus::Cancelled;
735                o
736            })
737            .collect();
738
739        info!("Canceled {} orders", canceled_orders.len());
740
741        Ok(canceled_orders)
742    }
743}
744
745#[cfg(test)]
746mod tests {
747    use super::*;
748
749    #[test]
750    #[allow(deprecated)]
751    fn test_get_nonce() {
752        let exchange = HyperLiquid::builder().testnet(true).build().unwrap();
753
754        let nonce1 = exchange.get_nonce();
755        std::thread::sleep(std::time::Duration::from_millis(10));
756        let nonce2 = exchange.get_nonce();
757
758        assert!(nonce2 > nonce1);
759    }
760}