1use crate::errors::{ProtocolError, Result};
2use crate::graphql;
3use crate::graphql::place_limit_order;
4use crate::graphql::place_market_order;
5use crate::types::neo::PublicKey as NeoPublicKey;
6use crate::types::PublicKey;
7use crate::types::{
8 Asset, AssetAmount, Blockchain, BuyOrSell, Nonce, OrderCancellationPolicy, OrderRate, Rate
9};
10use crate::utils::pad_zeros;
11use graphql_client::GraphQLQuery;
12use std::convert::TryInto;
13
14use super::super::signer::Signer;
15use super::super::{general_canonical_string, RequestPayloadSignature, State};
16use super::blockchain::{btc, eth, neo, FillOrder};
17use super::types::{
18 LimitOrderConstructor, LimitOrderRequest,
19 MarketOrderConstructor, MarketOrderRequest,
20 PayloadNonces
21};
22
23use tokio::sync::RwLock;
24use std::sync::Arc;
25
26type LimitOrderMutation = graphql_client::QueryBody<place_limit_order::Variables>;
27type MarketOrderMutation = graphql_client::QueryBody<place_market_order::Variables>;
28type LimitBlockchainSignatures = Vec<Option<place_limit_order::BlockchainSignature>>;
29type MarketBlockchainSignatures = Vec<Option<place_market_order::BlockchainSignature>>;
30
31impl LimitOrderRequest {
32 pub async fn make_constructor(&self, state: Arc<RwLock<State>>) -> Result<LimitOrderConstructor> {
35 let state = state.read().await;
36 let market = state.get_market(&self.market)?;
37
38 let amount_of_a = market.asset_a.with_amount(&self.amount)?;
40
41 let format_user_price = pad_zeros(&self.price, market.asset_b.precision)?;
44 let b_per_a: Rate = OrderRate::new(&format_user_price)?.into();
45
46 let a_per_b = b_per_a.invert_rate(None)?;
47
48 let amount_of_b = amount_of_a.exchange_at(&b_per_a, market.asset_b)?;
49
50 let (source, rate, destination) = match self.buy_or_sell {
51 BuyOrSell::Buy => {
52 (amount_of_b, a_per_b.clone(), market.asset_a)
54 }
55 BuyOrSell::Sell => {
56 (amount_of_a.clone(), b_per_a.clone(), market.asset_b)
58 }
59 };
60
61 Ok(LimitOrderConstructor {
62 client_order_id: self.client_order_id.clone(),
63 me_amount: amount_of_a,
64 me_rate: b_per_a,
65 market: market.clone(),
66 buy_or_sell: self.buy_or_sell,
67 cancellation_policy: self.cancellation_policy,
68 allow_taker: self.allow_taker,
69 source,
70 destination,
71 rate,
72 })
73 }
74}
75
76
77impl MarketOrderRequest {
78 pub async fn make_constructor(&self, state: Arc<RwLock<State>>) -> Result<MarketOrderConstructor> {
81 let state = state.read().await;
82
83 let market = match state.get_market(&self.market) {
84 Ok(market) => market,
85 Err(_) => {
86 let reverse_market: Vec<&str> = self.market.split('_').rev().collect();
87 let reverse_market = reverse_market.join("_");
88 match state.get_market(&reverse_market) {
89 Ok(market) => market.invert(),
90 Err(err) => return Err(err)
91 }
92 }
93 };
94
95 let source = market.asset_a.with_amount(&self.amount)?;
96 let destination = market.asset_b;
97
98 Ok(MarketOrderConstructor {
99 client_order_id: self.client_order_id.clone(),
100 me_amount: source.clone(),
101 market: market.clone(),
102 source,
103 destination,
104 })
105 }
106}
107
108fn map_crosschain(nonce: Nonce, chain: Blockchain, asset: Asset) -> Nonce {
111 if asset.blockchain() == chain {
112 nonce
113 } else {
114 Nonce::Crosschain
115 }
116}
117
118impl LimitOrderConstructor {
119 pub fn make_fill_order(
121 &self,
122 chain: Blockchain,
123 pub_key: &PublicKey,
124 nonces: &PayloadNonces,
125 order_precision: u32
126 ) -> Result<FillOrder> {
127 let min_order = self.rate.clone();
130 let max_order = Rate::MaxOrderRate;
131 let amount = self.source.amount.clone();
133 let min_order = min_order
134 .subtract_fee(Rate::MaxFeeRate.to_bigdecimal()?)?
135 .round(order_precision as i64 + 2)
136 .into();
137 let fee_rate = Rate::MinFeeRate; match chain {
140 Blockchain::Ethereum => Ok(FillOrder::Ethereum(eth::FillOrder::new(
141 pub_key.to_address()?.try_into()?,
142 self.source.asset.into(),
143 self.destination.into(),
144 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
145 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
146 amount,
147 min_order,
148 max_order,
149 fee_rate,
150 nonces.order_nonce,
151 ))),
152 Blockchain::Bitcoin => Ok(FillOrder::Bitcoin(btc::FillOrder::new(
153 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
154 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
155 ))),
156 Blockchain::NEO => {
157 let neo_pub_key: NeoPublicKey = pub_key.clone().try_into()?;
159 let neo_order = neo::FillOrder::new(
160 neo_pub_key,
161 self.source.asset.into(),
162 self.destination.into(),
163 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
164 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
165 amount,
166 min_order,
167 max_order,
168 fee_rate,
169 nonces.order_nonce,
170 );
171 Ok(FillOrder::NEO(neo_order))
172 }
173 }
174 }
175
176 pub fn blockchain_signatures(
179 &self,
180 signer: &Signer,
181 nonces: &[PayloadNonces],
182 order_precision: u32,
183 fee_precision: u32
184 ) -> Result<LimitBlockchainSignatures> {
185 let mut order_payloads = Vec::new();
186 let blockchains = self.market.blockchains();
187 for blockchain in blockchains {
188 let pub_key = signer.child_public_key(blockchain)?;
189 for nonce_group in nonces {
190 let fill_order = self.make_fill_order(blockchain, &pub_key, nonce_group, order_precision)?;
191 order_payloads.push(Some(fill_order.to_blockchain_signature(signer, order_precision, fee_precision)?))
192 }
193 }
194 Ok(order_payloads)
195 }
196
197 pub fn graphql_request(
200 &self,
201 current_time: i64,
202 affiliate: Option<String>,
203 ) -> Result<place_limit_order::Variables> {
204 let cancel_at = match self.cancellation_policy {
205 OrderCancellationPolicy::GoodTilTime(time) => Some(format!("{:?}", time)),
206 _ => None,
207 };
208 let order_args = place_limit_order::Variables {
209 payload: place_limit_order::PlaceLimitOrderParams {
210 client_order_id: self.client_order_id.clone(),
211 allow_taker: self.allow_taker,
212 buy_or_sell: self.buy_or_sell.into(),
213 cancel_at: cancel_at,
214 cancellation_policy: self.cancellation_policy.into(),
215 market_name: self.market.market_name(),
216 amount: self.me_amount.clone().try_into()?,
217 nonce_from: 1234,
219 nonce_to: 1234,
220 nonce_order: (current_time as u32) as i64, timestamp: current_time,
222 limit_price: place_limit_order::CurrencyPriceParams {
223 currency_a: self.market.asset_b.asset.name().to_string(),
226 currency_b: self.market.asset_a.asset.name().to_string(),
227 amount: self.me_rate.to_bigdecimal()?.to_string(),
228 },
229 blockchain_signatures: vec![],
230 },
231 affiliate,
232 signature: RequestPayloadSignature::empty().into(),
233 };
234 Ok(order_args)
235 }
236
237 pub fn sign_graphql_request(
238 &self,
239 mut variables: place_limit_order::Variables,
240 nonces: Vec<PayloadNonces>,
241 signer: &Signer,
242 order_precision: u32,
243 fee_precision: u32) -> Result<place_limit_order::Variables>
244 {
245 let client_order_id = variables.payload.client_order_id;
246 variables.payload.client_order_id = None;
247 let bc_sigs = self.blockchain_signatures(signer, &nonces, order_precision, fee_precision)?;
248 variables.payload.blockchain_signatures = bc_sigs;
249 let canonical_string = limit_order_canonical_string(&variables)?;
251 let sig: place_limit_order::Signature =
252 signer.sign_canonical_string(&canonical_string).into();
253 variables.signature = sig;
254 variables.payload.client_order_id = client_order_id;
255 Ok(variables)
256 }
257
258 pub fn signed_graphql_request(
261 &self,
262 nonces: Vec<PayloadNonces>,
263 current_time: i64,
264 affiliate: Option<String>,
265 signer: &Signer,
266 order_precision: u32,
267 fee_precision: u32
268 ) -> Result<LimitOrderMutation> {
269 let request = self.sign_graphql_request(self.graphql_request(current_time, affiliate)?, nonces, signer, order_precision, fee_precision)?;
270 Ok(graphql::PlaceLimitOrder::build_query(request))
271 }
272
273 pub async fn make_payload_nonces(
276 &self,
277 state: Arc<RwLock<State>>,
278 current_time: i64,
279 ) -> Result<Vec<PayloadNonces>> {
280 let state = state.read().await;
281 let asset_nonces = state.asset_nonces.as_ref()
282 .ok_or(ProtocolError("Asset nonce map does not exist"))?;
283 let (from, to) = match self.buy_or_sell {
284 BuyOrSell::Buy => (
285 self.market.asset_b.asset.name(),
286 self.market.asset_a.asset.name(),
287 ),
288 BuyOrSell::Sell => (
289 self.market.asset_a.asset.name(),
290 self.market.asset_b.asset.name(),
291 ),
292 };
293 let nonce_froms: Vec<Nonce> = asset_nonces
294 .get(from)
295 .ok_or(ProtocolError("Asset nonce for source does not exist"))?
296 .iter()
297 .map(|nonce| Nonce::Value(*nonce))
298 .collect();
299 let nonce_tos: Vec<Nonce> = asset_nonces
300 .get(to)
301 .ok_or(ProtocolError(
302 "Asset nonce for destination a does not exist",
303 ))?
304 .iter()
305 .map(|nonce| Nonce::Value(*nonce))
306 .collect();
307 let mut nonce_combinations = Vec::new();
308 for nonce_from in &nonce_froms {
309 for nonce_to in &nonce_tos {
310 nonce_combinations.push(PayloadNonces {
311 nonce_from: *nonce_from,
312 nonce_to: *nonce_to,
313 order_nonce: Nonce::Value(current_time as u32),
314 })
315 }
316 }
317 Ok(nonce_combinations)
318 }
319}
320
321impl MarketOrderConstructor {
322 pub fn make_fill_order(
324 &self,
325 chain: Blockchain,
326 pub_key: &PublicKey,
327 nonces: &PayloadNonces,
328 ) -> Result<FillOrder> {
329 let min_order = Rate::MinOrderRate;
332 let max_order = Rate::MaxOrderRate;
333 let amount = self.source.amount.clone();
335 let fee_rate = Rate::MinOrderRate; match chain {
338 Blockchain::Ethereum => Ok(FillOrder::Ethereum(eth::FillOrder::new(
339 pub_key.to_address()?.try_into()?,
340 self.source.asset.into(),
341 self.destination.into(),
342 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
343 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
344 amount,
345 min_order,
346 max_order,
347 fee_rate,
348 nonces.order_nonce,
349 ))),
350 Blockchain::Bitcoin => Ok(FillOrder::Bitcoin(btc::FillOrder::new(
351 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
352 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
353 ))),
354 Blockchain::NEO => {
355 let neo_pub_key: NeoPublicKey = pub_key.clone().try_into()?;
357 let neo_order = neo::FillOrder::new(
358 neo_pub_key,
359 self.source.asset.into(),
360 self.destination.into(),
361 map_crosschain(nonces.nonce_from, chain, self.source.asset.into()),
362 map_crosschain(nonces.nonce_to, chain, self.destination.into()),
363 amount,
364 min_order,
365 max_order,
366 fee_rate,
367 nonces.order_nonce,
368 );
369 Ok(FillOrder::NEO(neo_order))
370 }
371 }
372 }
373
374 pub fn blockchain_signatures(
377 &self,
378 signer: &Signer,
379 nonces: &[PayloadNonces],
380 order_precision: u32,
381 fee_precision: u32
382 ) -> Result<MarketBlockchainSignatures> {
383 let mut order_payloads = Vec::new();
384 let blockchains = self.market.blockchains();
385 for blockchain in blockchains {
386 let pub_key = signer.child_public_key(blockchain)?;
387 for nonce_group in nonces {
388 let fill_order = self.make_fill_order(blockchain, &pub_key, nonce_group)?;
389 order_payloads.push(Some(fill_order.to_market_blockchain_signature(signer, order_precision, fee_precision)?))
390 }
391 }
392 Ok(order_payloads)
393 }
394
395 pub fn graphql_request(
398 &self,
399 current_time: i64,
400 affiliate: Option<String>,
401 ) -> Result<place_market_order::Variables> {
402 let order_args = place_market_order::Variables {
403 payload: place_market_order::PlaceMarketOrderParams {
404 buy_or_sell: BuyOrSell::Sell.into(),
405 client_order_id: self.client_order_id.clone(),
406 market_name: self.market.market_name(),
407 amount: self.me_amount.clone().try_into()?,
408 nonce_from: Some(0),
410 nonce_to: Some(0),
411 nonce_order: (current_time as u32) as i64, timestamp: current_time,
413 blockchain_signatures: vec![],
414 },
415 affiliate,
416 signature: RequestPayloadSignature::empty().into(),
417 };
418 Ok(order_args)
419 }
420
421 pub fn sign_graphql_request(
422 &self,
423 mut variables: place_market_order::Variables,
424 nonces: Vec<PayloadNonces>,
425 signer: &Signer,
426 order_precision: u32,
427 fee_precision: u32
428 ) -> Result<place_market_order::Variables> {
429 let bc_sigs = self.blockchain_signatures(signer, &nonces, order_precision, fee_precision)?;
431 variables.payload.blockchain_signatures = bc_sigs;
432 let canonical_string = market_order_canonical_string(&variables)?;
434 let sig: place_market_order::Signature =
435 signer.sign_canonical_string(&canonical_string).into();
436 variables.signature = sig;
437 Ok(variables)
438 }
439
440 pub fn signed_graphql_request(
443 &self,
444 nonces: Vec<PayloadNonces>,
445 current_time: i64,
446 affiliate: Option<String>,
447 signer: &Signer,
448 order_precision: u32,
449 fee_precision: u32
450 ) -> Result<MarketOrderMutation> {
451 let request = self.sign_graphql_request(self.graphql_request(current_time, affiliate)?, nonces, signer, order_precision, fee_precision)?;
452 Ok(graphql::PlaceMarketOrder::build_query(request))
453 }
454
455 pub async fn make_payload_nonces(
458 &self,
459 state: Arc<RwLock<State>>,
460 current_time: i64,
461 ) -> Result<Vec<PayloadNonces>> {
462 let state = state.read().await;
463 let asset_nonces = state.asset_nonces.as_ref()
464 .ok_or(ProtocolError("Asset nonce map does not exist"))?;
465 let (from, to) = (
466 self.market.asset_a.asset.name(),
467 self.market.asset_b.asset.name(),
468 );
469 let nonce_froms: Vec<Nonce> = asset_nonces
470 .get(from)
471 .ok_or(ProtocolError("Asset nonce for source does not exist"))?
472 .iter()
473 .map(|nonce| Nonce::Value(*nonce))
474 .collect();
475 let nonce_tos: Vec<Nonce> = asset_nonces
476 .get(to)
477 .ok_or(ProtocolError(
478 "Asset nonce for destination a does not exist",
479 ))?
480 .iter()
481 .map(|nonce| Nonce::Value(*nonce))
482 .collect();
483 let mut nonce_combinations = Vec::new();
484 for nonce_from in &nonce_froms {
485 for nonce_to in &nonce_tos {
486 nonce_combinations.push(PayloadNonces {
487 nonce_from: *nonce_from,
488 nonce_to: *nonce_to,
489 order_nonce: Nonce::Value(current_time as u32),
490 })
491 }
492 }
493 Ok(nonce_combinations)
494 }
495}
496
497pub fn limit_order_canonical_string(variables: &place_limit_order::Variables) -> Result<String> {
498 let serialized_all = serde_json::to_string(variables).map_err(|_|ProtocolError("Failed to serialize limit order into canonical string"))?;
499
500 Ok(general_canonical_string(
501 "place_limit_order".to_string(),
502 serde_json::from_str(&serialized_all).map_err(|_|ProtocolError("Failed to deserialize limit order into canonical string"))?,
503 vec!["blockchain_signatures".to_string()],
504 ))
505}
506
507pub fn market_order_canonical_string(variables: &place_market_order::Variables) -> Result<String> {
508 let serialized_all = serde_json::to_string(variables).map_err(|_|ProtocolError("Failed to serialize market order into canonical string"))?;
509
510 Ok(general_canonical_string(
511 "place_market_order".to_string(),
512 serde_json::from_str(&serialized_all).map_err(|_|ProtocolError("Failed to deserialize market order into canonical string"))?,
513 vec!["blockchain_signatures".to_string()],
514 ))
515}
516
517impl Into<place_market_order::OrderBuyOrSell> for BuyOrSell {
518 fn into(self) -> place_market_order::OrderBuyOrSell {
519 match self {
520 BuyOrSell::Buy => place_market_order::OrderBuyOrSell::BUY,
521 BuyOrSell::Sell => place_market_order::OrderBuyOrSell::SELL,
522 }
523 }
524}
525
526impl From<RequestPayloadSignature> for place_market_order::Signature {
527 fn from(sig: RequestPayloadSignature) -> Self {
528 place_market_order::Signature {
529 signed_digest: sig.signed_digest,
530 public_key: sig.public_key,
531 }
532 }
533}
534
535impl TryInto<place_market_order::CurrencyAmountParams> for AssetAmount {
536 type Error = ProtocolError;
537 fn try_into(self) -> Result<place_market_order::CurrencyAmountParams> {
538 Ok(place_market_order::CurrencyAmountParams {
539 amount: pad_zeros(
540 &self.amount.to_bigdecimal().to_string(),
541 self.amount.precision,
542 )?,
543 currency: self.asset.asset.name().to_string(),
545 })
546 }
547}
548
549
550
551impl Into<place_limit_order::OrderBuyOrSell> for BuyOrSell {
552 fn into(self) -> place_limit_order::OrderBuyOrSell {
553 match self {
554 BuyOrSell::Buy => place_limit_order::OrderBuyOrSell::BUY,
555 BuyOrSell::Sell => place_limit_order::OrderBuyOrSell::SELL,
556 }
557 }
558}
559
560impl From<RequestPayloadSignature> for place_limit_order::Signature {
561 fn from(sig: RequestPayloadSignature) -> Self {
562 place_limit_order::Signature {
563 signed_digest: sig.signed_digest,
564 public_key: sig.public_key,
565 }
566 }
567}
568
569impl From<OrderCancellationPolicy> for place_limit_order::OrderCancellationPolicy {
570 fn from(policy: OrderCancellationPolicy) -> Self {
571 match policy {
572 OrderCancellationPolicy::FillOrKill => {
573 place_limit_order::OrderCancellationPolicy::FILL_OR_KILL
574 }
575 OrderCancellationPolicy::GoodTilCancelled => {
576 place_limit_order::OrderCancellationPolicy::GOOD_TIL_CANCELLED
577 }
578 OrderCancellationPolicy::GoodTilTime(_) => {
579 place_limit_order::OrderCancellationPolicy::GOOD_TIL_TIME
580 }
581 OrderCancellationPolicy::ImmediateOrCancel => {
582 place_limit_order::OrderCancellationPolicy::IMMEDIATE_OR_CANCEL
583 }
584 }
585 }
586}
587
588impl TryInto<place_limit_order::CurrencyAmountParams> for AssetAmount {
589 type Error = ProtocolError;
590 fn try_into(self) -> Result<place_limit_order::CurrencyAmountParams> {
591 Ok(place_limit_order::CurrencyAmountParams {
592 amount: pad_zeros(
593 &self.amount.to_bigdecimal().to_string(),
594 self.amount.precision,
595 )?,
596 currency: self.asset.asset.name().to_string(),
598 })
599 }
600}