1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::str::FromStr;

use ex3_node_error::OtherError;
use ex3_node_types::chain::Chain;
use ex3_timestamp::TimeInNs;
use num_bigint::BigUint;
use num_traits::Num;

use crate::{PayloadDecoder, Result};

impl PayloadDecoder {
    /// Decode the payload to a withdrawal
    ///
    /// Payload plain text:
    /// ```text
    //      subject: EX3 withdrawal
    //      wallet_id: 0x1234
    //      nonce: : 0x1234
    //      chain: 0x01 // wallet signature chain,used for verification
    //      data.chain: 0x12
    //      data.network: 0x1
    //      data.address: 1jlfjad9s8fuoj2l34ljljfaosdfjdasf
    //      data.asset_id: 0x1234
    //      data.amount: 983459845
    //      data.fee: 3454
    /// ```
    /// The payload should be in the following format:
    /// `{chain}|{data.chain}|{data.network}|{data.address}|{data.asset_id}|{data.amount}|{data.fee}`
    ///
    /// Used for the following transactions:
    /// - [TransactionType::Withdrawal]
    pub fn decode_to_withdrawal(payload: &[u8]) -> Result<ex3_node_types::transaction::Withdrawal> {
        let payload_str = String::from_utf8(payload.as_ref().to_vec()).expect("should success");

        let hexes = payload_str.split('|').collect::<Vec<&str>>();

        let err = OtherError::new("Invalid payload");

        if hexes.len() != 7 {
            return Err(err.clone().into());
        }

        let sign_chain = BigUint::from_str_radix(hexes[0], 16).map_err(|_| err.clone())?;
        let chain = BigUint::from_str_radix(hexes[1], 16).map_err(|_| err.clone())?;
        let network = u8::from_str_radix(hexes[2], 16).map_err(|_| err.clone())?;
        let to_address: String = hexes[3].to_string();
        let asset_id = BigUint::from_str_radix(hexes[4], 16).map_err(|_| err.clone())?;
        let amount = BigUint::from_str(hexes[5]).map_err(|_| err.clone())?;
        let fee = BigUint::from_str(hexes[6]).map_err(|_| err.clone())?;

        let withdrawal = ex3_node_types::transaction::Withdrawal {
            sign_chain: sign_chain.into(),
            chain: chain.into(),
            network,
            to: to_address,
            asset_id: asset_id.into(),
            amount: amount.into(),
            fee: fee.into(),
        };

        Ok(withdrawal)
    }

    /// Decode the payload to a force withdrawal
    ///
    /// Payload plain text:
    /// ```text
    ///     subject: EX3 force withdrawal
    ///     wallet_id: 0x1234
    ///     chain: 0x12
    ///     data.chain: 0x12
    ///     data.network: 0x1
    ///     data.address: 1jlfjad9s8fuoj2l34ljljfaosdfjdasf
    ///     data.asset_id: 0x1234
    ///     data.amount: 0x81723123
    ///     data.fee: 0x8132
    ///     data.timestamp: 0x9817239321323
    /// ```
    /// The payload should be in the following format:
    /// ``{chain}|{data.chain}|{data.network}|{data.address}|{data.asset_id}|{data.amount}|{data.fee}|{data.timestamp}``
    ///
    /// Used for the following transactions:
    /// - [TransactionType::ForceWithdrawal]
    pub fn decode_to_force_withdrawal(
        payload: &[u8],
    ) -> Result<ex3_node_types::transaction::ForceWithdrawal> {
        let payload_str = String::from_utf8(payload.as_ref().to_vec()).expect("should success");

        let hexes = payload_str.split('|').collect::<Vec<&str>>();

        let err = OtherError::new("Invalid payload");

        if hexes.len() != 8 {
            return Err(err.clone().into());
        }

        let sign_chain_type = BigUint::from_str_radix(hexes[0], 16).map_err(|_| err.clone())?;
        let chain_type = BigUint::from_str_radix(hexes[1], 16).map_err(|_| err.clone())?;
        let network = BigUint::from_str_radix(hexes[1], 16).map_err(|_| err.clone())?;
        let to_address: String = String::from_utf8(hex::decode(hexes[2]).map_err(|_| err.clone())?)
            .map_err(|_| err.clone())?;
        let asset_id = BigUint::from_str_radix(hexes[3], 16).map_err(|_| err.clone())?;
        let amount = BigUint::from_str(hexes[4]).map_err(|_| err.clone())?;
        let fee = BigUint::from_str(hexes[5]).map_err(|_| err.clone())?;
        let timestamp = u64::from_str_radix(hexes[6], 16).map_err(|_| err.clone())?;

        let force_withdrawal = ex3_node_types::transaction::ForceWithdrawal {
            sign_chain_type: sign_chain_type.into(),
            chain: Chain {
                r#type: chain_type.into(),
                network,
            },
            to: to_address,
            asset_id: asset_id.into(),
            amount: amount.into(),
            fee: fee.into(),
            timestamp: TimeInNs(timestamp),
        };

        Ok(force_withdrawal)
    }
}