tonlib_core/message/jetton/
transfer_notification.rs

1use num_bigint::BigUint;
2
3use super::JETTON_TRANSFER_NOTIFICATION;
4use crate::cell::{ArcCell, Cell, CellBuilder, EitherCellLayout, EMPTY_ARC_CELL};
5use crate::message::{HasOpcode, TonMessage, TonMessageError};
6use crate::TonAddress;
7
8/// Creates a body for jetton transfer notification according to TL-B schema:
9///
10/// ```raw
11///transfer_notification#7362d09c query_id:uint64 amount:(VarUInteger 16)
12///                               sender:MsgAddress forward_payload:(Either Cell ^Cell)
13///                               = InternalMsgBody;
14/// ```
15#[derive(Clone, Debug, PartialEq)]
16pub struct JettonTransferNotificationMessage {
17    /// should be equal with request's query_id.
18    pub query_id: u64,
19    /// amount of transferred jettons.
20    pub amount: BigUint,
21    /// is address of the previous owner of transferred jettons.
22    pub sender: TonAddress,
23    ///  optional custom data that should be sent to the destination address.
24    pub forward_payload: ArcCell,
25
26    pub forward_payload_layout: EitherCellLayout,
27}
28
29impl JettonTransferNotificationMessage {
30    pub fn new(sender: &TonAddress, amount: &BigUint) -> Self {
31        JettonTransferNotificationMessage {
32            query_id: 0,
33            amount: amount.clone(),
34            sender: sender.clone(),
35            forward_payload: EMPTY_ARC_CELL.clone(),
36            forward_payload_layout: EitherCellLayout::Native,
37        }
38    }
39
40    pub fn with_forward_payload(&mut self, forward_payload: ArcCell) -> &mut Self {
41        self.forward_payload = forward_payload;
42        self
43    }
44
45    pub fn set_either_cell_layout(&mut self, layout: EitherCellLayout) -> &mut Self {
46        self.forward_payload_layout = layout;
47        self
48    }
49}
50
51impl TonMessage for JettonTransferNotificationMessage {
52    fn build(&self) -> Result<Cell, TonMessageError> {
53        let mut builder = CellBuilder::new();
54        builder.store_u32(32, Self::opcode())?;
55        builder.store_u64(64, self.query_id)?;
56        builder.store_coins(&self.amount)?;
57        builder.store_address(&self.sender)?;
58        builder
59            .store_either_cell_or_cell_ref(&self.forward_payload, self.forward_payload_layout)?;
60
61        Ok(builder.build()?)
62    }
63
64    fn parse(cell: &Cell) -> Result<Self, TonMessageError> {
65        let mut parser = cell.parser();
66
67        let opcode: u32 = parser.load_u32(32)?;
68        let query_id = parser.load_u64(64)?;
69
70        let amount = parser.load_coins()?;
71        let sender = parser.load_address()?;
72        let forward_payload = parser.load_either_cell_or_cell_ref()?;
73        parser.ensure_empty()?;
74
75        let result = JettonTransferNotificationMessage {
76            query_id,
77            amount,
78            sender,
79            forward_payload,
80            forward_payload_layout: EitherCellLayout::Native,
81        };
82        result.verify_opcode(opcode)?;
83
84        Ok(result)
85    }
86}
87
88impl HasOpcode for JettonTransferNotificationMessage {
89    fn set_query_id(&mut self, query_id: u64) {
90        self.query_id = query_id;
91    }
92
93    fn query_id(&self) -> u64 {
94        self.query_id
95    }
96
97    fn opcode() -> u32 {
98        JETTON_TRANSFER_NOTIFICATION
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use std::str::FromStr;
105    use std::sync::Arc;
106
107    use num_bigint::BigUint;
108
109    use crate::cell::{BagOfCells, Cell, EitherCellLayout};
110    use crate::message::{JettonTransferNotificationMessage, TonMessage};
111    use crate::TonAddress;
112
113    const JETTON_TRANSFER_NOTIFICATION_MSG: &str = "b5ee9c720101020100a60001647362d09c000000d2c7ceef23401312d008003be20895401cd8539741eb7815d5e63b3429014018d7e5f7800de16a984f27730100dd25938561800f2465b65c76b1b562f32423676970b431319419d5f45ffd2eeb2155ce6ab7eacc78ee0250ef0300077c4112a8039b0a72e83d6f02babcc766852028031afcbef001bc2d5309e4ee700257a672371a90e149b7d25864dbfd44827cc1e8a30df1b1e0c4338502ade2ad96";
114    const TRANSFER_NOTIFICATION_PAYLOAD: &str = "25938561800f2465b65c76b1b562f32423676970b431319419d5f45ffd2eeb2155ce6ab7eacc78ee0250ef0300077c4112a8039b0a72e83d6f02babcc766852028031afcbef001bc2d5309e4ee700257a672371a90e149b7d25864dbfd44827cc1e8a30df1b1e0c4338502ade2ad94";
115
116    #[test]
117    fn test_jetton_transfer_notification_parser() -> anyhow::Result<()> {
118        let boc = BagOfCells::parse_hex(JETTON_TRANSFER_NOTIFICATION_MSG)?;
119        let cell = boc.single_root()?;
120
121        let expected_jetton_transfer_notification_msg = JettonTransferNotificationMessage {
122            query_id: 905295359779,
123            amount: BigUint::from(20000000u64),
124            sender: TonAddress::from_str("EQAd8QRKoA5sKcug9bwK6vMdmhSAoAxr8vvABvC1TCeTude5")?,
125            forward_payload: Arc::new(Cell::new(
126                hex::decode(TRANSFER_NOTIFICATION_PAYLOAD).unwrap(),
127                886,
128                vec![],
129                false,
130            )?),
131            forward_payload_layout: EitherCellLayout::Native,
132        };
133        let result_jetton_transfer_msg = JettonTransferNotificationMessage::parse(&cell)?;
134
135        assert_eq!(
136            expected_jetton_transfer_notification_msg,
137            result_jetton_transfer_msg
138        );
139        Ok(())
140    }
141
142    #[test]
143    fn test_jetton_transfer_notification_builder() -> anyhow::Result<()> {
144        let jetton_transfer_notification_msg = JettonTransferNotificationMessage {
145            query_id: 905295359779,
146            amount: BigUint::from(20000000u64),
147            sender: TonAddress::from_str("EQAd8QRKoA5sKcug9bwK6vMdmhSAoAxr8vvABvC1TCeTude5")
148                .unwrap(),
149            forward_payload: Arc::new(Cell::new(
150                hex::decode(TRANSFER_NOTIFICATION_PAYLOAD).unwrap(),
151                886,
152                vec![],
153                false,
154            )?),
155            forward_payload_layout: EitherCellLayout::Native,
156        };
157
158        let result_cell = jetton_transfer_notification_msg.build()?;
159
160        let expected_boc_serialized = hex::decode(JETTON_TRANSFER_NOTIFICATION_MSG)?;
161        let result_boc_serialized = BagOfCells::from_root(result_cell).serialize(false)?;
162
163        assert_eq!(expected_boc_serialized, result_boc_serialized);
164        Ok(())
165    }
166}