Skip to main content

nash_protocol/protocol/place_orders/
request.rs

1use crate::errors::Result;
2use crate::graphql::place_limit_order;
3use crate::graphql::place_market_order;
4use super::super::State;
5use super::types::{
6    LimitOrdersConstructor, LimitOrdersRequest,
7    MarketOrdersConstructor, MarketOrdersRequest
8};
9
10use tokio::sync::RwLock;
11use std::sync::Arc;
12
13use std::collections::HashMap;
14use crate::protocol::multi_request::DynamicQueryBody;
15
16impl LimitOrdersRequest {
17    // Buy or sell `amount` of `A` in price of `B` for an A/B market. Returns a builder struct
18    // of `LimitOrderConstructor` that can be used to create smart contract and graphql payloads
19    pub async fn make_constructor(&self, state: Arc<RwLock<State>>) -> Result<LimitOrdersConstructor> {
20        let mut constructors = Vec::new();
21        for request in &self.requests {
22            constructors.push(request.make_constructor(state.clone()).await?);
23        }
24        Ok(LimitOrdersConstructor { constructors })
25    }
26}
27
28impl MarketOrdersRequest {
29    // Buy or sell `amount` of `A` in price of `B` for an A/B market. Returns a builder struct
30    // of `MarketOrderConstructor` that can be used to create smart contract and graphql payloads
31    pub async fn make_constructor(&self, state: Arc<RwLock<State>>) -> Result<MarketOrdersConstructor> {
32        let mut constructors = Vec::new();
33        for request in &self.requests {
34            constructors.push(request.make_constructor(state.clone()).await?);
35        }
36        Ok(MarketOrdersConstructor { constructors })
37    }
38}
39
40impl LimitOrdersConstructor {
41    /// Create a GraphQL request with everything filled in besides blockchain order payloads
42    /// and signatures (for both the overall request and blockchain payloads)
43    pub fn graphql_request(
44        &self,
45        current_time: i64,
46        affiliate: Option<String>,
47    ) -> Result<Vec<place_limit_order::Variables>> {
48        let mut variables = Vec::new();
49        for (index, variable) in self.constructors.iter().enumerate() {
50            variables.push(variable.graphql_request(current_time + index as i64, affiliate.clone())?);
51        }
52        Ok(variables)
53    }
54
55    /// Create a signed GraphQL request with blockchain payloads that can be submitted
56    /// to Nash
57    pub async fn signed_graphql_request(
58        &self,
59        current_time: i64,
60        affiliate: Option<String>,
61        state: Arc<RwLock<State>>,
62        order_precision: u32,
63        fee_precision: u32
64    ) -> Result<DynamicQueryBody> {
65        let variables = self.graphql_request(current_time, affiliate)?;
66        let mut map = HashMap::new();
67        let mut params = String::new();
68        let mut calls = String::new();
69        for (index, (variable, constructor)) in variables.into_iter().zip(self.constructors.iter()).enumerate() {
70            // FIXME: This current_time + index for nonces is replicated in graphql_request. We would benefit to abstract this logic somewhere.
71            let nonces = constructor.make_payload_nonces(state.clone(), current_time + index as i64).await?;
72            let state = state.read().await;
73            let signer = state.signer()?;
74            let variable = constructor.sign_graphql_request(variable, nonces, signer, order_precision, fee_precision)?;
75
76            // FIXME: This is also replicated in MarketOrdersConstructor::signed_graphql_request
77            let payload = format!("payload{}", index);
78            let signature = format!("signature{}", index);
79            let affiliate = format!("affiliate{}", index);
80            params = if index == 0 { params } else { format!("{}, ", params)};
81            params = format!("{}${}: PlaceLimitOrderParams!, ${}: Signature!, ${}: AffiliateDeveloperCode", params, payload, signature, affiliate);
82            calls = format!(r#"
83                {}
84                response{}: placeLimitOrder(payload: ${}, signature: ${}, affiliateDeveloperCode: ${}) {{
85                    id
86                    status
87                    ordersTillSignState,
88                    buyOrSell,
89                    market {{
90                        name
91                    }},
92                    placedAt,
93                    type
94                }}
95                "#, calls, index, payload, signature, affiliate);
96            map.insert(payload, serde_json::to_value(variable.payload).unwrap());
97            map.insert(signature, serde_json::to_value(variable.signature).unwrap());
98            map.insert(affiliate, serde_json::to_value(variable.affiliate).unwrap());
99        }
100        Ok(DynamicQueryBody {
101            variables: map,
102            operation_name: "PlaceLimitOrder",
103            query: format!(r#"
104                mutation PlaceLimitOrder({}) {{
105                    {}
106                }}
107            "#, params, calls)
108        })
109    }
110}
111
112impl MarketOrdersConstructor {
113    /// Create a GraphQL request with everything filled in besides blockchain order payloads
114    /// and signatures (for both the overall request and blockchain payloads)
115    pub fn graphql_request(
116        &self,
117        current_time: i64,
118        affiliate: Option<String>,
119    ) -> Result<Vec<place_market_order::Variables>> {
120        let mut variables = Vec::new();
121        for (index, variable) in self.constructors.iter().enumerate() {
122            variables.push(variable.graphql_request(current_time + index as i64, affiliate.clone())?);
123        }
124        Ok(variables)
125    }
126
127    /// Create a signed GraphQL request with blockchain payloads that can be submitted
128    /// to Nash
129    pub async fn signed_graphql_request(
130        &self,
131        current_time: i64,
132        affiliate: Option<String>,
133        state: Arc<RwLock<State>>,
134        order_precision: u32,
135        fee_precision: u32
136    ) -> Result<DynamicQueryBody> {
137        let variables = self.graphql_request(current_time, affiliate)?;
138        let mut map = HashMap::new();
139        let mut params = String::new();
140        let mut calls = String::new();
141        for (index, (variable, constructor)) in variables.into_iter().zip(self.constructors.iter()).enumerate() {
142            // FIXME: This current_time + index for nonces is replicated in graphql_request. We would benefit to abstract this logic somewhere.
143            let nonces = constructor.make_payload_nonces(state.clone(), current_time + index as i64).await?;
144            let state = state.read().await;
145            let signer = state.signer()?;
146            let variable = constructor.sign_graphql_request(variable, nonces, signer, order_precision, fee_precision)?;
147
148            let payload = format!("payload{}", index);
149            let signature = format!("signature{}", index);
150            let affiliate = format!("affiliate{}", index);
151            params = if index == 0 { params } else { format!("{}, ", params)};
152            params = format!("{}${}: PlaceMarketOrderParams!, ${}: Signature!, ${}: AffiliateDeveloperCode", params, payload, signature, affiliate);
153            calls = format!(r#"
154                {}
155                response{}: placeMarketOrder(payload: ${}, signature: ${}, affiliateDeveloperCode: ${}) {{
156                    id
157                    status
158                    ordersTillSignState,
159                    buyOrSell,
160                    market {{
161                        name
162                    }},
163                    placedAt,
164                    type
165                }}
166                "#, calls, index, payload, signature, affiliate);
167            map.insert(payload, serde_json::to_value(variable.payload).unwrap());
168            map.insert(signature, serde_json::to_value(variable.signature).unwrap());
169            map.insert(affiliate, serde_json::to_value(variable.affiliate).unwrap());
170        }
171        Ok(DynamicQueryBody {
172            variables: map,
173            operation_name: "PlaceMarketOrder",
174            query: format!(r#"
175                mutation PlaceMarketOrder({}) {{
176                    {}
177                }}
178            "#, params, calls)
179        })
180    }
181}