tonlib_core/message/
transfer.rs

1use std::sync::Arc;
2
3use super::{
4    CommonMsgInfo, ExternalIncomingMessage, ExternalOutgoingMessage, InternalMessage, TonMessage,
5    TonMessageError,
6};
7use crate::cell::{ArcCell, Cell, CellBuilder, EitherCellLayout};
8
9#[derive(Clone, Debug, PartialEq)]
10pub struct TransferMessage {
11    pub common_msg_info: CommonMsgInfo,
12    pub state_init: Option<ArcCell>,
13    pub body: ArcCell,
14}
15
16impl TransferMessage {
17    pub fn new(common_msg_info: CommonMsgInfo, body: ArcCell) -> Self {
18        TransferMessage {
19            common_msg_info,
20            state_init: None,
21            body,
22        }
23    }
24
25    pub fn with_state_init(&mut self, state_init: Cell) -> &mut Self {
26        self.with_state_init_ref(&Arc::new(state_init))
27    }
28
29    pub fn with_state_init_ref(&mut self, state_init: &ArcCell) -> &mut Self {
30        self.state_init = Some(state_init.clone());
31        self
32    }
33}
34
35impl TonMessage for TransferMessage {
36    fn build(&self) -> Result<Cell, TonMessageError> {
37        let mut builder = CellBuilder::new();
38
39        match &self.common_msg_info {
40            CommonMsgInfo::InternalMessage(m) => {
41                builder.store_bit(false)?; // bit0 (is_external)
42                builder.store_bit(m.ihr_disabled)?; // ihr_disabled
43                builder.store_bit(m.bounce)?; // bounce
44                builder.store_bit(m.bounced)?; // bounced
45                builder.store_address(&m.src)?; // src_addr
46                builder.store_address(&m.dest)?; // dest_addr
47                builder.store_coins(&m.value)?; // value
48                builder.store_bit(false)?; // currency_coll
49                builder.store_coins(&m.ihr_fee)?; // ihr_fees
50                builder.store_coins(&m.fwd_fee)?; // fwd_fees
51                builder.store_u64(64, m.created_lt)?; // created_lt
52                builder.store_u32(32, m.created_at)?; // created_at
53            }
54            CommonMsgInfo::ExternalIncomingMessage(m) => {
55                builder.store_bit(true)?; // bit0 (is_external)
56                builder.store_bit(false)?; // bit0 (is_outgoing)
57                builder.store_address(&m.src)?;
58                builder.store_address(&m.dest)?;
59                builder.store_coins(&m.import_fee)?;
60            }
61            CommonMsgInfo::ExternalOutgoingMessage(m) => {
62                builder.store_bit(true)?; // bit0 (is_external)
63                builder.store_bit(true)?; // bit0 (is_outgoing)
64                builder.store_address(&m.src)?;
65                builder.store_address(&m.dest)?;
66                builder.store_u64(64, m.created_lt)?; // created_lt
67                builder.store_u32(32, m.created_at)?; // created_at
68            }
69        }
70        if let Some(state_init) = &self.state_init {
71            builder.store_bit(true)?;
72            builder.store_either_cell_or_cell_ref(state_init, EitherCellLayout::ToRef)?;
73        } else {
74            builder.store_bit(false)?;
75        }
76
77        builder.store_either_cell_or_cell_ref(&self.body, EitherCellLayout::ToRef)?;
78
79        Ok(builder.build()?)
80    }
81
82    fn parse(cell: &Cell) -> Result<Self, TonMessageError> {
83        let mut parser = cell.parser();
84        //   TODO: Review structure of transfer message
85        let bit0 = parser.load_bit()?;
86
87        // internal message
88        let common_msg_info = if !bit0 {
89            let ihr_disabled = parser.load_bit()?;
90            let bounce = parser.load_bit()?;
91            let bounced = parser.load_bit()?;
92            let src = parser.load_address()?;
93            let dest = parser.load_address()?;
94            let value = parser.load_coins()?;
95            let _currency_coll = parser.load_bit()?;
96            let ihr_fee = parser.load_coins()?;
97            let fwd_fee = parser.load_coins()?;
98            let created_lt = parser.load_u64(64)?;
99            let created_at = parser.load_u32(32)?;
100
101            CommonMsgInfo::InternalMessage(InternalMessage {
102                ihr_disabled,
103                bounce,
104                bounced,
105                src,
106                dest,
107                value,
108                ihr_fee,
109                fwd_fee,
110                created_lt,
111                created_at,
112            })
113        } else {
114            let bit1 = parser.load_bit()?;
115
116            if !bit1 {
117                let src = parser.load_address()?;
118                let dest = parser.load_address()?;
119                let import_fee = parser.load_coins()?;
120
121                CommonMsgInfo::ExternalIncomingMessage(ExternalIncomingMessage {
122                    src,
123                    dest,
124                    import_fee,
125                })
126            } else {
127                let src = parser.load_address()?;
128                let dest = parser.load_address()?;
129
130                let created_lt = parser.load_u64(64)?;
131                let created_at = parser.load_u32(32)?;
132
133                CommonMsgInfo::ExternalOutgoingMessage(ExternalOutgoingMessage {
134                    src,
135                    dest,
136                    created_lt,
137                    created_at,
138                })
139            }
140        };
141        let state_init = if parser.load_bit()? {
142            Some(parser.load_either_cell_or_cell_ref()?)
143        } else {
144            None
145        };
146        let body = parser.load_either_cell_or_cell_ref()?;
147
148        parser.ensure_empty()?;
149
150        Ok(TransferMessage {
151            common_msg_info,
152            state_init,
153            body,
154        })
155    }
156}
157
158#[cfg(test)]
159mod test {
160    use num_bigint::BigUint;
161
162    use super::TransferMessage;
163    use crate::cell::{Cell, EMPTY_ARC_CELL};
164    use crate::message::{CommonMsgInfo, InternalMessage, TonMessage};
165    use crate::tlb_types::tlb::TLB;
166    use crate::TonAddress;
167
168    fn make_internal() -> CommonMsgInfo {
169        CommonMsgInfo::InternalMessage(InternalMessage {
170            ihr_disabled: true,
171            bounce: false,
172            bounced: false,
173            src: TonAddress::NULL,
174            dest: TonAddress::NULL,
175            value: BigUint::from(100000_u32),
176            ihr_fee: BigUint::from(0_u32),
177            fwd_fee: BigUint::from(1000_u32),
178            created_lt: 0,
179            created_at: 0,
180        })
181    }
182
183    #[test]
184    fn test_transfer_message_parser() -> anyhow::Result<()> {
185        let transfer_message = TransferMessage::new(make_internal(), EMPTY_ARC_CELL.clone());
186        let transfer_cell = transfer_message.build()?;
187        let transfer_parsed = TransferMessage::parse(&transfer_cell)?;
188        assert_eq!(transfer_message, transfer_parsed);
189        Ok(())
190    }
191
192    #[test]
193    fn test_transfer_msg_with_state_init() -> anyhow::Result<()> {
194        let mut transfer_message = TransferMessage::new(make_internal(), EMPTY_ARC_CELL.clone());
195        let state_init = Cell::from_boc_hex("b5ee9c720102160100030400020134020100510000082f29a9a31738dd3a33f904d35e2f4f6f9af2d2f9c563c05faa6bb0b12648d5632083ea3f89400114ff00f4a413f4bcf2c80b03020120090404f8f28308d71820d31fd31fd31f02f823bbf264ed44d0d31fd31fd3fff404d15143baf2a15151baf2a205f901541064f910f2a3f80024a4c8cb1f5240cb1f5230cbff5210f400c9ed54f80f01d30721c0009f6c519320d74a96d307d402fb00e830e021c001e30021c002e30001c0039130e30d03a4c8cb1f12cb1fcbff08070605000af400c9ed54006c810108d718fa00d33f305224810108f459f2a782106473747270748018c8cb05cb025005cf165003fa0213cb6acb1f12cb3fc973fb000070810108d718fa00d33fc8542047810108f451f2a782106e6f746570748018c8cb05cb025006cf165004fa0214cb6a12cb1fcb3fc973fb0002006ed207fa00d4d422f90005c8ca0715cbffc9d077748018c8cb05cb0222cf165005fa0214cb6b12ccccc973fb00c84014810108f451f2a702020148130a0201200c0b0059bd242b6f6a2684080a06b90fa0218470d4080847a4937d29910ce6903e9ff9837812801b7810148987159f31840201200e0d0011b8c97ed44d0d70b1f8020158120f02012011100019af1df6a26840106b90eb858fc00019adce76a26840206b90eb85ffc0003db29dfb513420405035c87d010c00b23281f2fff274006040423d029be84c6002e6d001d0d3032171b0925f04e022d749c120925f04e002d31f218210706c7567bd22821064737472bdb0925f05e003fa403020fa4401c8ca07cbffc9d0ed44d0810140d721f404305c810108f40a6fa131b3925f07e005d33fc8258210706c7567ba923830e30d03821064737472ba925f06e30d1514008a5004810108f45930ed44d0810140d720c801cf16f400c9ed540172b08e23821064737472831eb17080185005cb055003cf1623fa0213cb6acb1fcb3fc98040fb00925f03e2007801fa00f40430f8276f2230500aa121bef2e0508210706c7567831eb17080185004cb0526cf1658fa0219f400cb6917cb1f5260cb3f20c98040fb0006")?;
196        transfer_message.with_state_init(state_init);
197
198        let transfer_cell = transfer_message.build()?;
199        let transfer_parsed = TransferMessage::parse(&transfer_cell)?;
200        assert_eq!(transfer_message, transfer_parsed);
201        assert_eq!(transfer_cell.to_boc_hex(false)?, "b5ee9c720102180100031e0002284030186a00101f400000000000000000000000070102020134030400000114ff00f4a413f4bcf2c80b0500510000082f29a9a31738dd3a33f904d35e2f4f6f9af2d2f9c563c05faa6bb0b12648d5632083ea3f89400201200607020148080904f8f28308d71820d31fd31fd31f02f823bbf264ed44d0d31fd31fd3fff404d15143baf2a15151baf2a205f901541064f910f2a3f80024a4c8cb1f5240cb1f5230cbff5210f400c9ed54f80f01d30721c0009f6c519320d74a96d307d402fb00e830e021c001e30021c002e30001c0039130e30d03a4c8cb1f12cb1fcbff0a0b0c0d02e6d001d0d3032171b0925f04e022d749c120925f04e002d31f218210706c7567bd22821064737472bdb0925f05e003fa403020fa4401c8ca07cbffc9d0ed44d0810140d721f404305c810108f40a6fa131b3925f07e005d33fc8258210706c7567ba923830e30d03821064737472ba925f06e30d0e0f0201201011006ed207fa00d4d422f90005c8ca0715cbffc9d077748018c8cb05cb0222cf165005fa0214cb6b12ccccc973fb00c84014810108f451f2a7020070810108d718fa00d33fc8542047810108f451f2a782106e6f746570748018c8cb05cb025006cf165004fa0214cb6a12cb1fcb3fc973fb0002006c810108d718fa00d33f305224810108f459f2a782106473747270748018c8cb05cb025005cf165003fa0213cb6acb1f12cb3fc973fb00000af400c9ed54007801fa00f40430f8276f2230500aa121bef2e0508210706c7567831eb17080185004cb0526cf1658fa0219f400cb6917cb1f5260cb3f20c98040fb0006008a5004810108f45930ed44d0810140d720c801cf16f400c9ed540172b08e23821064737472831eb17080185005cb055003cf1623fa0213cb6acb1fcb3fc98040fb00925f03e202012012130059bd242b6f6a2684080a06b90fa0218470d4080847a4937d29910ce6903e9ff9837812801b7810148987159f318402015814150011b8c97ed44d0d70b1f8003db29dfb513420405035c87d010c00b23281f2fff274006040423d029be84c6002012016170019adce76a26840206b90eb85ffc00019af1df6a26840106b90eb858fc0");
202        Ok(())
203    }
204}