waves_rust/model/order/
mod.rs

1pub mod v3;
2pub mod v4;
3
4use crate::error::Error::UnsupportedOperation;
5use crate::error::{Error, Result};
6use crate::model::{Address, Amount, AssetId, ByteString, Id, Proof, PublicKey};
7use crate::util::{Base58, BinarySerializer, Hash, JsonDeserializer, JsonSerializer};
8use crate::waves_proto::order::Sender::SenderPublicKey;
9use crate::waves_proto::{Amount as ProtoAmount, AssetPair, Order as ProtoOrder};
10use serde_json::{Map, Value};
11
12use self::v3::{OrderInfoV3, OrderV3};
13use self::v4::{OrderInfoV4, OrderV4};
14
15use super::PrivateKey;
16
17#[derive(Eq, PartialEq, Clone, Debug)]
18pub enum OrderInfo {
19    V3(v3::OrderInfoV3),
20    V4(v4::OrderInfoV4),
21}
22
23// could it be done using trait?
24impl OrderInfo {
25    pub fn id(&self) -> Id {
26        match self {
27            Self::V3(order) => order.id(),
28            Self::V4(order) => order.id(),
29        }
30    }
31
32    pub fn chain_id(&self) -> u8 {
33        match self {
34            Self::V3(order) => order.chain_id(),
35            Self::V4(order) => order.chain_id(),
36        }
37    }
38
39    pub fn order_type(&self) -> OrderType {
40        match self {
41            Self::V3(order) => order.order_type(),
42            Self::V4(order) => order.order_type(),
43        }
44    }
45
46    pub fn version(&self) -> u8 {
47        match self {
48            Self::V3(order) => order.version(),
49            Self::V4(order) => order.version(),
50        }
51    }
52
53    pub fn sender(&self) -> PublicKey {
54        match self {
55            Self::V3(order) => order.sender(),
56            Self::V4(order) => order.sender(),
57        }
58    }
59
60    pub fn amount(&self) -> Amount {
61        match self {
62            Self::V3(order) => order.amount(),
63            Self::V4(order) => order.amount(),
64        }
65    }
66
67    pub fn price(&self) -> Amount {
68        match self {
69            Self::V3(order) => order.price(),
70            Self::V4(order) => order.price(),
71        }
72    }
73
74    pub fn fee(&self) -> Amount {
75        match self {
76            Self::V3(order) => order.fee(),
77            Self::V4(order) => order.fee(),
78        }
79    }
80
81    pub fn matcher(&self) -> PublicKey {
82        match self {
83            Self::V3(order) => order.matcher(),
84            Self::V4(order) => order.matcher(),
85        }
86    }
87
88    pub fn timestamp(&self) -> u64 {
89        match self {
90            Self::V3(order) => order.timestamp(),
91            Self::V4(order) => order.timestamp(),
92        }
93    }
94
95    pub fn expiration(&self) -> u64 {
96        match self {
97            Self::V3(order) => order.expiration(),
98            Self::V4(order) => order.expiration(),
99        }
100    }
101
102    pub fn price_mode(&self) -> PriceMode {
103        match self {
104            Self::V3(_) => PriceMode::AssetDecimals,
105            Self::V4(order) => order.price_mode(),
106        }
107    }
108
109    pub fn proofs(&self) -> Vec<Proof> {
110        match self {
111            Self::V3(order) => order.proofs(),
112            Self::V4(order) => order.proofs(),
113        }
114    }
115}
116
117impl TryFrom<&Value> for OrderInfo {
118    type Error = Error;
119
120    fn try_from(order_json: &Value) -> Result<Self> {
121        let signed_order: SignedOrder = order_json.try_into()?;
122        signed_order.try_into()
123    }
124}
125
126#[derive(Eq, PartialEq, Clone, Debug)]
127pub struct SignedOrder {
128    order: Order,
129    proofs: Vec<Proof>,
130}
131
132impl SignedOrder {
133    pub fn new(order: Order, proofs: Vec<Proof>) -> SignedOrder {
134        SignedOrder { order, proofs }
135    }
136
137    pub fn order(&self) -> Order {
138        self.order.clone()
139    }
140
141    pub fn proofs(&self) -> Vec<Proof> {
142        self.proofs.clone()
143    }
144
145    pub fn id(&self) -> Result<Id> {
146        Ok(Id::from_bytes(&Hash::blake(&self.bytes()?)?))
147    }
148
149    pub fn bytes(&self) -> Result<Vec<u8>> {
150        BinarySerializer::order_body_bytes(&self.order())
151    }
152
153    pub fn to_json(&self) -> Result<Value> {
154        JsonSerializer::serialize_signed_order(self)
155    }
156}
157
158impl TryFrom<&Value> for SignedOrder {
159    type Error = Error;
160
161    fn try_from(signed_order_json: &Value) -> Result<Self> {
162        let order: Order = signed_order_json.try_into()?;
163
164        let signature = Proof::new(Base58::decode(
165            &JsonDeserializer::safe_to_string_from_field(signed_order_json, "signature")?,
166        )?);
167
168        Ok(SignedOrder::new(order, vec![signature]))
169    }
170}
171
172impl TryFrom<&SignedOrder> for Value {
173    type Error = Error;
174
175    fn try_from(signed_order: &SignedOrder) -> Result<Self> {
176        let order = signed_order.order();
177        let order_type = match order.order_type() {
178            OrderType::Buy => "buy",
179            OrderType::Sell => "sell",
180        };
181        let mut order_json = Map::new();
182        order_json.insert("orderType".to_owned(), order_type.into());
183        order_json.insert("version".to_owned(), order.version().into());
184        order_json.insert(
185            "senderPublicKey".to_owned(),
186            order.sender().encoded().into(),
187        );
188        order_json.insert(
189            "sender".to_owned(),
190            order.sender().address(order.chain_id())?.encoded().into(),
191        );
192        let mut asset_pair_json = Map::new();
193        asset_pair_json.insert(
194            "amountAsset".to_owned(),
195            order
196                .amount()
197                .asset_id()
198                .map(|asset| asset.encoded().into())
199                .unwrap_or(Value::Null),
200        );
201        asset_pair_json.insert(
202            "priceAsset".to_owned(),
203            order
204                .price()
205                .asset_id()
206                .map(|asset| asset.encoded().into())
207                .unwrap_or(Value::Null),
208        );
209
210        order_json.insert("assetPair".to_owned(), asset_pair_json.into());
211        order_json.insert("amount".to_owned(), order.amount().value().into());
212        order_json.insert("price".to_owned(), order.price().value().into());
213        order_json.insert("matcherFee".to_owned(), order.fee().value().into());
214        order_json.insert(
215            "matcherPublicKey".to_owned(),
216            order.matcher().encoded().into(),
217        );
218        order_json.insert(
219            "matcherFeeAssetId".to_owned(),
220            order
221                .fee()
222                .asset_id()
223                .map(|asset| asset.encoded().into())
224                .unwrap_or(Value::Null),
225        );
226        order_json.insert("timestamp".to_owned(), order.timestamp().into());
227        order_json.insert("expiration".to_owned(), order.expiration().into());
228        let signature = signed_order.proofs[0].encoded();
229        order_json.insert("signature".to_owned(), signature.clone().into());
230        order_json.insert("proofs".to_owned(), vec![Value::String(signature)].into());
231
232        match order {
233            Order::V3(_) => {}
234            Order::V4(order_v4) => {
235                let price_mode = match order_v4.price_mode() {
236                    PriceMode::Default => "default",
237                    PriceMode::FixedDecimals => "fixedDecimals",
238                    PriceMode::AssetDecimals => "assetDecimals",
239                };
240                order_json.insert("priceMode".to_owned(), price_mode.into());
241            }
242        }
243
244        Ok(order_json.into())
245    }
246}
247
248impl TryFrom<&SignedOrder> for ProtoOrder {
249    type Error = Error;
250
251    fn try_from(signed_order: &SignedOrder) -> Result<Self> {
252        let order = signed_order.order();
253
254        match order {
255            Order::V3(_) => Err(UnsupportedOperation(
256                "Order version 3 can't be transformed into protobuf message".to_owned(),
257            )),
258            Order::V4(order) => Ok(ProtoOrder {
259                chain_id: order.chain_id() as i32,
260                matcher_public_key: order.matcher().bytes(),
261                asset_pair: map_asset_pair(&order),
262                order_side: map_order_side(&order),
263                amount: order.amount().value() as i64,
264                price: order.price().value() as i64,
265                timestamp: order.timestamp() as i64,
266                expiration: order.expiration() as i64,
267                matcher_fee: map_matcher_fee(&order),
268                version: order.version() as i32,
269                proofs: signed_order
270                    .proofs()
271                    .iter()
272                    .map(|proof| proof.bytes())
273                    .collect(),
274                price_mode: map_price_mode(&order),
275                sender: Some(SenderPublicKey(order.sender().bytes())),
276            }),
277        }
278    }
279}
280
281impl TryFrom<SignedOrder> for OrderInfo {
282    type Error = Error;
283
284    fn try_from(signed_order: SignedOrder) -> Result<Self> {
285        match signed_order.order() {
286            Order::V3(order) => signed_order.id().map(|id| {
287                OrderInfo::V3(OrderInfoV3::new(
288                    id,
289                    order.chain_id(),
290                    order.timestamp(),
291                    order.sender(),
292                    order.fee(),
293                    order.order_type(),
294                    order.amount(),
295                    order.price(),
296                    order.matcher(),
297                    order.expiration(),
298                    signed_order.proofs(),
299                ))
300            }),
301            Order::V4(order) => signed_order.id().map(|id| {
302                OrderInfo::V4(OrderInfoV4::new(
303                    id,
304                    order.chain_id(),
305                    order.timestamp(),
306                    order.sender(),
307                    order.fee(),
308                    order.order_type(),
309                    order.amount(),
310                    order.price(),
311                    order.matcher(),
312                    order.expiration(),
313                    signed_order.proofs(),
314                    order.price_mode(),
315                ))
316            }),
317        }
318    }
319}
320
321#[derive(Eq, PartialEq, Clone, Debug)]
322pub enum Order {
323    V3(v3::OrderV3),
324    V4(v4::OrderV4),
325}
326
327// could it be done using trait?
328impl Order {
329    #[allow(clippy::too_many_arguments)]
330    pub fn v3(
331        chain_id: u8,
332        timestamp: u64,
333        sender: PublicKey,
334        fee: Amount,
335        order_type: OrderType,
336        amount: Amount,
337        price: Amount,
338        matcher: PublicKey,
339        expiration: u64,
340    ) -> Self {
341        Self::V3(OrderV3::new(
342            chain_id, timestamp, sender, fee, order_type, amount, price, matcher, expiration,
343        ))
344    }
345
346    #[allow(clippy::too_many_arguments)]
347    pub fn v4(
348        chain_id: u8,
349        timestamp: u64,
350        sender: PublicKey,
351        fee: Amount,
352        order_type: OrderType,
353        amount: Amount,
354        price: Amount,
355        matcher: PublicKey,
356        expiration: u64,
357        price_mode: PriceMode,
358    ) -> Self {
359        Self::V4(OrderV4::new(
360            chain_id, timestamp, sender, fee, order_type, amount, price, matcher, expiration,
361            price_mode,
362        ))
363    }
364
365    pub fn id(&self) -> Result<Id> {
366        match self {
367            Self::V3(order) => order.id(),
368            Self::V4(order) => order.id(),
369        }
370    }
371
372    pub fn chain_id(&self) -> u8 {
373        match self {
374            Self::V3(order) => order.chain_id(),
375            Self::V4(order) => order.chain_id(),
376        }
377    }
378
379    pub fn order_type(&self) -> OrderType {
380        match self {
381            Self::V3(order) => order.order_type(),
382            Self::V4(order) => order.order_type(),
383        }
384    }
385
386    pub fn version(&self) -> u8 {
387        match self {
388            Self::V3(order) => order.version(),
389            Self::V4(order) => order.version(),
390        }
391    }
392
393    pub fn sender(&self) -> PublicKey {
394        match self {
395            Self::V3(order) => order.sender(),
396            Self::V4(order) => order.sender(),
397        }
398    }
399
400    pub fn amount(&self) -> Amount {
401        match self {
402            Self::V3(order) => order.amount(),
403            Self::V4(order) => order.amount(),
404        }
405    }
406
407    pub fn price(&self) -> Amount {
408        match self {
409            Self::V3(order) => order.price(),
410            Self::V4(order) => order.price(),
411        }
412    }
413
414    pub fn fee(&self) -> Amount {
415        match self {
416            Self::V3(order) => order.fee(),
417            Self::V4(order) => order.fee(),
418        }
419    }
420
421    pub fn matcher(&self) -> PublicKey {
422        match self {
423            Self::V3(order) => order.matcher(),
424            Self::V4(order) => order.matcher(),
425        }
426    }
427
428    pub fn timestamp(&self) -> u64 {
429        match self {
430            Self::V3(order) => order.timestamp(),
431            Self::V4(order) => order.timestamp(),
432        }
433    }
434
435    pub fn expiration(&self) -> u64 {
436        match self {
437            Self::V3(order) => order.expiration(),
438            Self::V4(order) => order.expiration(),
439        }
440    }
441
442    pub fn sign(&self, private_key: &PrivateKey) -> Result<SignedOrder> {
443        match self {
444            Self::V3(order) => order.sign(private_key),
445            Self::V4(order) => order.sign(private_key),
446        }
447    }
448
449    pub fn default_expiration(current_time: u64) -> u64 {
450        current_time + (30 * 24 * 60 * 60 * 1000)
451    }
452}
453
454#[derive(Eq, PartialEq, Clone, Debug)]
455pub enum OrderType {
456    Buy,
457    Sell,
458}
459
460#[derive(Eq, PartialEq, Clone, Debug)]
461pub enum PriceMode {
462    Default,
463    FixedDecimals,
464    AssetDecimals,
465}
466
467impl TryFrom<&Order> for ProtoOrder {
468    type Error = Error;
469
470    fn try_from(order: &Order) -> Result<Self> {
471        match order {
472            Order::V3(_) => Err(UnsupportedOperation(
473                "Order version 3 can't be transformed into protobuf message".to_owned(),
474            )),
475            Order::V4(order) => Ok(ProtoOrder {
476                chain_id: order.chain_id() as i32,
477                matcher_public_key: order.matcher().bytes(),
478                asset_pair: map_asset_pair(order),
479                order_side: map_order_side(order),
480                amount: order.amount().value() as i64,
481                price: order.price().value() as i64,
482                timestamp: order.timestamp() as i64,
483                expiration: order.expiration() as i64,
484                matcher_fee: map_matcher_fee(order),
485                version: order.version() as i32,
486                proofs: vec![],
487                price_mode: map_price_mode(order),
488                sender: Some(SenderPublicKey(order.sender().bytes())),
489            }),
490        }
491    }
492}
493
494impl TryFrom<&Value> for Order {
495    type Error = Error;
496
497    fn try_from(order_json: &Value) -> Result<Self> {
498        let order_type =
499            match JsonDeserializer::safe_to_string_from_field(order_json, "orderType")?.as_str() {
500                "buy" => OrderType::Buy,
501                "sell" => OrderType::Sell,
502                _ => return Err(UnsupportedOperation("unknown order type".to_owned())),
503            };
504        let version = JsonDeserializer::safe_to_int_from_field(order_json, "version")? as u8;
505        let timestamp = JsonDeserializer::safe_to_int_from_field(order_json, "timestamp")?;
506        let sender_public_key = PublicKey::from_string(
507            &JsonDeserializer::safe_to_string_from_field(order_json, "senderPublicKey")?,
508        )?;
509
510        let sender = Address::from_string(&JsonDeserializer::safe_to_string_from_field(
511            order_json, "sender",
512        )?)?;
513
514        let matcher_fee = JsonDeserializer::safe_to_int_from_field(order_json, "matcherFee")?;
515        let matcher_fee_asset_id = match order_json["matcherFeeAssetId"].as_str() {
516            Some(asset) => Some(AssetId::from_string(asset)?),
517            None => None,
518        };
519
520        let amount_asset = match order_json["assetPair"]["amountAsset"].as_str() {
521            Some(asset) => Some(AssetId::from_string(asset)?),
522            None => None,
523        };
524        let amount = JsonDeserializer::safe_to_int_from_field(order_json, "amount")?;
525
526        let price_asset = match order_json["assetPair"]["priceAsset"].as_str() {
527            Some(asset) => Some(AssetId::from_string(asset)?),
528            None => None,
529        };
530
531        let price = JsonDeserializer::safe_to_int_from_field(order_json, "price")?;
532
533        let matcher_public_key = PublicKey::from_string(
534            &JsonDeserializer::safe_to_string_from_field(order_json, "matcherPublicKey")?,
535        )?;
536
537        let expiration = JsonDeserializer::safe_to_int_from_field(order_json, "expiration")?;
538
539        let price_mode = match order_json["priceMode"].as_str() {
540            Some(price_mode) => match price_mode {
541                "default" => PriceMode::Default,
542                "fixedDecimals" => PriceMode::FixedDecimals,
543                "assetDecimals" => PriceMode::AssetDecimals,
544                _ => return Err(UnsupportedOperation("unknown price mode".to_owned())),
545            },
546            // https://docs.waves.tech/en/blockchain/order#json-representation
547            None => PriceMode::Default,
548        };
549
550        match version {
551            3 => Ok(Order::V3(v3::OrderV3::new(
552                sender.chain_id(),
553                timestamp as u64,
554                sender_public_key,
555                Amount::new(matcher_fee as u64, matcher_fee_asset_id),
556                order_type,
557                Amount::new(amount as u64, amount_asset),
558                Amount::new(price as u64, price_asset),
559                matcher_public_key,
560                expiration as u64,
561            ))),
562            4 => Ok(Order::V4(v4::OrderV4::new(
563                sender.chain_id(),
564                timestamp as u64,
565                sender_public_key,
566                Amount::new(matcher_fee as u64, matcher_fee_asset_id),
567                order_type,
568                Amount::new(amount as u64, amount_asset),
569                Amount::new(price as u64, price_asset),
570                matcher_public_key,
571                expiration as u64,
572                price_mode,
573            ))),
574            _ => Err(Error::UnsupportedOrderVersion),
575        }
576    }
577}
578
579fn map_asset_pair(order: &OrderV4) -> Option<AssetPair> {
580    Some(AssetPair {
581        amount_asset_id: order
582            .amount()
583            .asset_id()
584            .map(|asset| asset.bytes())
585            .unwrap_or_default(),
586        price_asset_id: order
587            .price()
588            .asset_id()
589            .map(|asset| asset.bytes())
590            .unwrap_or_default(),
591    })
592}
593
594fn map_order_side(order: &OrderV4) -> i32 {
595    match order.order_type() {
596        OrderType::Buy => 0,
597        OrderType::Sell => 1,
598    }
599}
600
601fn map_matcher_fee(order: &OrderV4) -> Option<ProtoAmount> {
602    Some(ProtoAmount {
603        asset_id: order
604            .fee()
605            .asset_id()
606            .map(|asset| asset.bytes())
607            .unwrap_or_default(),
608        amount: order.fee().value() as i64,
609    })
610}
611
612fn map_price_mode(order: &OrderV4) -> i32 {
613    match order.price_mode() {
614        PriceMode::Default => 0,
615        PriceMode::FixedDecimals => 1,
616        PriceMode::AssetDecimals => 2,
617    }
618}