tonlib_core/message/nft/
transfer.rs1use num_bigint::BigUint;
2use num_traits::Zero;
3
4use super::NFT_TRANSFER;
5use crate::cell::{ArcCell, Cell, CellBuilder, EitherCellLayout, EMPTY_ARC_CELL};
6use crate::message::{HasOpcode, TonMessage, TonMessageError, WithForwardPayload, ZERO_COINS};
7use crate::TonAddress;
8
9#[derive(Clone, Debug, PartialEq)]
22pub struct NftTransferMessage {
23 pub query_id: u64,
25 pub new_owner: TonAddress,
27 pub response_destination: TonAddress,
29 pub custom_payload: Option<ArcCell>,
31 pub forward_ton_amount: BigUint,
33 pub forward_payload: ArcCell,
35
36 pub forward_payload_layout: EitherCellLayout,
37}
38
39impl NftTransferMessage {
40 pub fn new(new_owner: &TonAddress) -> Self {
41 NftTransferMessage {
42 query_id: 0,
43 new_owner: new_owner.clone(),
44 response_destination: TonAddress::NULL,
45 custom_payload: None,
46 forward_ton_amount: ZERO_COINS.clone(),
47 forward_payload: EMPTY_ARC_CELL.clone(),
48 forward_payload_layout: EitherCellLayout::Native,
49 }
50 }
51
52 pub fn with_response_destination(&mut self, response_destination: &TonAddress) -> &mut Self {
53 self.response_destination = response_destination.clone();
54 self
55 }
56
57 pub fn with_custom_payload(&mut self, custom_payload: ArcCell) -> &mut Self {
58 self.custom_payload = Some(custom_payload);
59 self
60 }
61}
62
63impl TonMessage for NftTransferMessage {
64 fn build(&self) -> Result<Cell, TonMessageError> {
65 if self.forward_ton_amount.is_zero() && self.forward_payload == EMPTY_ARC_CELL.clone() {
66 return Err(TonMessageError::ForwardTonAmountIsNegative);
67 }
68
69 let mut builder = CellBuilder::new();
70 builder.store_u32(32, Self::opcode())?;
71 builder.store_u64(64, self.query_id)?;
72
73 builder.store_address(&self.new_owner)?;
74 builder.store_address(&self.response_destination)?;
75 builder.store_ref_cell_optional(self.custom_payload.as_ref())?;
76 builder.store_coins(&self.forward_ton_amount)?;
77 builder
78 .store_either_cell_or_cell_ref(&self.forward_payload, self.forward_payload_layout)?;
79 Ok(builder.build()?)
80 }
81
82 fn parse(cell: &Cell) -> Result<Self, TonMessageError> {
83 let mut parser = cell.parser();
84
85 let opcode: u32 = parser.load_u32(32)?;
86 let query_id = parser.load_u64(64)?;
87
88 let new_owner = parser.load_address()?;
89 let response_destination = parser.load_address()?;
90 let custom_payload = parser.load_maybe_cell_ref()?;
91 let forward_ton_amount = parser.load_coins()?;
92 let forward_payload = parser.load_either_cell_or_cell_ref()?;
93 parser.ensure_empty()?;
94
95 let result = NftTransferMessage {
96 query_id,
97 new_owner,
98 response_destination,
99 custom_payload,
100 forward_ton_amount,
101 forward_payload,
102 forward_payload_layout: EitherCellLayout::Native,
103 };
104 result.verify_opcode(opcode)?;
105
106 Ok(result)
107 }
108}
109
110impl WithForwardPayload for NftTransferMessage {
111 fn set_forward_payload(&mut self, forward_payload: ArcCell, forward_ton_amount: BigUint) {
112 self.forward_payload = forward_payload;
113 self.forward_ton_amount = forward_ton_amount;
114 }
115}
116
117impl HasOpcode for NftTransferMessage {
118 fn set_query_id(&mut self, query_id: u64) {
119 self.query_id = query_id;
120 }
121
122 fn query_id(&self) -> u64 {
123 self.query_id
124 }
125
126 fn opcode() -> u32 {
127 NFT_TRANSFER
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use std::sync::Arc;
134
135 use lazy_static::lazy_static;
136 use num_bigint::BigUint;
137
138 use crate::cell::{ArcCell, BagOfCells, Cell, EitherCellLayout};
139 use crate::message::{
140 HasOpcode, NftTransferMessage, TonMessage, TonMessageError, WithForwardPayload,
141 };
142 use crate::TonAddress;
143
144 const NFT_TRANSFER_MSG: &str="b5ee9c7201010101006f0000d95fcc3d140000000000000000800e20aaf07ad251d1800fe45e3af334769b7b2069d3ab2ea6c9ee0f73dfd072a21000a1b4b24b6a66313f3e0b49d095f3e8f4294af504b3a0f7b99290129f3aaafcc47312d0040544f4e506c616e65747320676966742077697468206c6f76658";
145 const NFT_TRANSFER_PAYLOAD_DATA: &str = "40544F4E506C616E65747320676966742077697468206C6F7665";
146 const NFT_TRANSFER_PAYLOAD_BIT_LEN: usize = 208;
147
148 lazy_static! {
149 static ref NFT_TRANSFER_PAYLOAD: ArcCell = Arc::new(
150 Cell::new(
151 hex::decode(NFT_TRANSFER_PAYLOAD_DATA).unwrap(),
152 NFT_TRANSFER_PAYLOAD_BIT_LEN,
153 vec![],
154 false,
155 )
156 .unwrap()
157 );
158 }
159 #[test]
160 fn test_ft_transfer_parser() -> Result<(), TonMessageError> {
161 let boc = BagOfCells::parse_hex(NFT_TRANSFER_MSG)?;
162 let cell = boc.single_root()?;
163
164 let result_nft_transfer_msg = NftTransferMessage::parse(&cell)?;
165
166 let forward_ton_amount = BigUint::from(10000000u64);
167 let expected_nft_transfer_msg = NftTransferMessage {
168 query_id: 0,
169 new_owner: TonAddress::from_hex_str(
170 "0:71055783d6928e8c007f22f1d799a3b4dbd9034e9d5975364f707b9efe839510",
171 )
172 .unwrap(),
173 response_destination: TonAddress::from_hex_str(
174 "0:286d2c92da998c4fcf82d274257cfa3d0a52bd412ce83dee64a404a7ceaabf31",
175 )
176 .unwrap(),
177 custom_payload: None,
178 forward_ton_amount,
179 forward_payload: NFT_TRANSFER_PAYLOAD.clone(),
180 forward_payload_layout: EitherCellLayout::Native,
181 };
182
183 assert_eq!(expected_nft_transfer_msg, result_nft_transfer_msg);
184 Ok(())
185 }
186
187 #[test]
188 fn test_nft_transfer_builder() -> anyhow::Result<()> {
189 let jetton_transfer_msg = NftTransferMessage::new(&TonAddress::from_hex_str(
190 "0:71055783d6928e8c007f22f1d799a3b4dbd9034e9d5975364f707b9efe839510",
191 )?)
192 .with_query_id(0)
193 .with_response_destination(&TonAddress::from_hex_str(
194 "0:286d2c92da998c4fcf82d274257cfa3d0a52bd412ce83dee64a404a7ceaabf31",
195 )?)
196 .with_forward_payload(BigUint::from(10000000u64), NFT_TRANSFER_PAYLOAD.clone())
197 .build();
198
199 let result_boc_serialized = BagOfCells::from_root(jetton_transfer_msg?).serialize(false)?;
200 let expected_boc_serialized = hex::decode(NFT_TRANSFER_MSG)?;
201
202 assert_eq!(expected_boc_serialized, result_boc_serialized);
203 Ok(())
204 }
205}