1use bitvec::{mem::bits_of, view::AsBits};
2use num_bigint::BigUint;
3use tlb_ton::{
4 Cell, EitherInlineOrRef, Error, MsgAddress, ParseFully, Ref, Same,
5 bits::{
6 NoArgs, Remainder, VarInt,
7 de::{BitReader, BitReaderExt, BitUnpack},
8 integer::ConstU32,
9 ser::{BitPack, BitWriter, BitWriterExt},
10 },
11 de::{CellDeserialize, CellParser, CellParserError},
12 either::Either,
13 ser::{CellBuilder, CellBuilderError, CellSerialize},
14};
15
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct JettonTransfer<P = Cell, F = Cell> {
26 pub query_id: u64,
27 pub amount: BigUint,
28 pub dst: MsgAddress,
29 pub response_dst: MsgAddress,
30 pub custom_payload: Option<P>,
31 pub forward_ton_amount: BigUint,
32 pub forward_payload: ForwardPayload<F>,
33}
34
35const JETTON_TRANSFER_TAG: u32 = 0x0f8a7ea5;
36
37impl<P, F> CellSerialize for JettonTransfer<P, F>
38where
39 P: CellSerialize<Args: NoArgs>,
40 F: CellSerialize<Args: NoArgs>,
41{
42 type Args = ();
43
44 fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
45 builder
46 .pack(JETTON_TRANSFER_TAG, ())?
48 .pack(self.query_id, ())?
50 .pack_as::<_, &VarInt<4>>(&self.amount, ())?
52 .pack(self.dst, ())?
54 .pack(self.response_dst, ())?
56 .store_as::<_, Option<Ref>>(self.custom_payload.as_ref(), NoArgs::EMPTY)?
58 .pack_as::<_, &VarInt<4>>(&self.forward_ton_amount, ())?
60 .store_as::<_, EitherInlineOrRef>(&self.forward_payload, NoArgs::EMPTY)?;
62
63 Ok(())
64 }
65}
66
67impl<'de, P, F> CellDeserialize<'de> for JettonTransfer<P, F>
68where
69 P: CellDeserialize<'de, Args: NoArgs>,
70 F: CellDeserialize<'de, Args: NoArgs>,
71{
72 type Args = ();
73
74 fn parse(parser: &mut CellParser<'de>, _: Self::Args) -> Result<Self, CellParserError<'de>> {
75 parser.unpack::<ConstU32<JETTON_TRANSFER_TAG>>(())?;
77 Ok(Self {
78 query_id: parser.unpack(())?,
80 amount: parser.unpack_as::<_, VarInt<4>>(())?,
82 dst: parser.unpack(())?,
84 response_dst: parser.unpack(())?,
86 custom_payload: parser.parse_as::<_, Option<Ref<ParseFully>>>(P::Args::EMPTY)?,
88 forward_ton_amount: parser.unpack_as::<_, VarInt<4>>(())?,
90 forward_payload: parser
92 .parse_as::<Either<ForwardPayload<F>, ForwardPayload<F>>, Either<ParseFully, Ref<ParseFully>>>(NoArgs::EMPTY)?
93 .into_inner(),
94 })
95 }
96}
97
98#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub enum ForwardPayload<T = Cell> {
101 Data(T),
102 Comment(ForwardPayloadComment),
103}
104
105impl<T> ForwardPayload<T> {
106 const COMMENT_PREFIX: u32 = 0x00000000;
107}
108
109impl<T> CellSerialize for ForwardPayload<T>
110where
111 T: CellSerialize<Args: NoArgs>,
112{
113 type Args = ();
114
115 #[inline]
116 fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
117 match self {
118 Self::Data(data) => builder.store(data, NoArgs::EMPTY)?,
119 Self::Comment(comment) => builder.pack(Self::COMMENT_PREFIX, ())?.pack(comment, ())?,
120 };
121 Ok(())
122 }
123}
124
125impl<'de, F> CellDeserialize<'de> for ForwardPayload<F>
126where
127 F: CellDeserialize<'de, Args: NoArgs>,
128{
129 type Args = ();
130
131 fn parse(parser: &mut CellParser<'de>, _: Self::Args) -> Result<Self, CellParserError<'de>> {
132 if parser.bits_left() >= bits_of::<u32>()
133 && parser.clone().unpack::<u32>(())? == Self::COMMENT_PREFIX
135 {
136 let _ = parser.unpack::<u32>(())?;
138 return parser.unpack(()).map(Self::Comment);
139 }
140 parser.parse(NoArgs::EMPTY).map(Self::Data)
141 }
142}
143
144#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub enum ForwardPayloadComment {
147 Text(String),
148 Binary(Vec<u8>),
149}
150
151impl ForwardPayloadComment {
152 const BINARY_PREFIX: u8 = 0xff;
153}
154
155impl BitPack for ForwardPayloadComment {
156 type Args = ();
157
158 #[inline]
159 fn pack<W>(&self, writer: &mut W, _: Self::Args) -> Result<(), W::Error>
160 where
161 W: BitWriter + ?Sized,
162 {
163 match self {
164 Self::Text(comment) => writer.write_bitslice(comment.as_bytes().as_bits())?,
165 Self::Binary(comment) => writer
166 .pack(Self::BINARY_PREFIX, ())?
167 .write_bitslice(comment.as_bits())?,
168 };
169 Ok(())
170 }
171}
172
173impl<'de> BitUnpack<'de> for ForwardPayloadComment {
174 type Args = ();
175
176 #[inline]
177 fn unpack<R>(reader: &mut R, _: Self::Args) -> Result<Self, R::Error>
178 where
179 R: BitReader<'de> + ?Sized,
180 {
181 let mut r = reader.checkpoint();
182 if r.bits_left() >= bits_of::<u8>() && r.unpack::<u8>(())? == Self::BINARY_PREFIX {
183 return r.unpack_as::<_, Remainder>(()).map(Self::Binary);
184 }
185 r.restore()
186 .unpack_as::<_, Remainder>(())
187 .map(Self::Text)
188 .map_err(Error::custom)
189 }
190}
191
192#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
199#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct JettonTransferNotification<P = Cell> {
201 pub query_id: u64,
202 pub amount: BigUint,
203 pub sender: MsgAddress,
204 pub forward_payload: ForwardPayload<P>,
205}
206
207const JETTON_TRANSFER_NOTIFICATION_TAG: u32 = 0x7362d09c;
208
209impl<P> CellSerialize for JettonTransferNotification<P>
210where
211 P: CellSerialize<Args: NoArgs>,
212{
213 type Args = ();
214
215 fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
216 builder
217 .pack(JETTON_TRANSFER_NOTIFICATION_TAG, ())?
218 .pack(self.query_id, ())?
219 .pack_as::<_, &VarInt<4>>(&self.amount, ())?
220 .pack(self.sender, ())?
221 .store_as::<Either<(), _>, Either<Same, Ref>>(
222 Either::Right(&self.forward_payload),
223 NoArgs::EMPTY,
224 )?;
225 Ok(())
226 }
227}
228
229impl<'de, P> CellDeserialize<'de> for JettonTransferNotification<P>
230where
231 P: CellDeserialize<'de, Args: NoArgs>,
232{
233 type Args = ();
234
235 fn parse(parser: &mut CellParser<'de>, _: Self::Args) -> Result<Self, CellParserError<'de>> {
236 parser.unpack::<ConstU32<JETTON_TRANSFER_NOTIFICATION_TAG>>(())?;
237 Ok(Self {
238 query_id: parser.unpack(())?,
239 amount: parser.unpack_as::<_, VarInt<4>>(())?,
240 sender: parser.unpack(())?,
241 forward_payload: parser
242 .parse_as::<Either<ForwardPayload<P>, ForwardPayload<P>>, Either<Same, Ref<ParseFully>>>(NoArgs::EMPTY)?
243 .into_inner(),
244 })
245 }
246}
247
248#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
255#[derive(Debug, Clone, PartialEq, Eq)]
256pub struct JettonBurn<P = Cell> {
257 pub query_id: u64,
258 pub amount: BigUint,
259 pub response_dst: MsgAddress,
260 pub custom_payload: Option<P>,
261}
262
263const JETTON_BURN_TAG: u32 = 0x595f07bc;
264
265impl<P> CellSerialize for JettonBurn<P>
266where
267 P: CellSerialize<Args: NoArgs>,
268{
269 type Args = ();
270
271 fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
272 builder
273 .pack(JETTON_BURN_TAG, ())?
274 .pack_as::<_, &VarInt<4>>(&self.amount, ())?
275 .pack(self.response_dst, ())?
276 .store_as::<_, Option<Ref>>(self.custom_payload.as_ref(), NoArgs::EMPTY)?;
277 Ok(())
278 }
279}
280
281impl<'de, P> CellDeserialize<'de> for JettonBurn<P>
282where
283 P: CellDeserialize<'de, Args: NoArgs>,
284{
285 type Args = ();
286
287 fn parse(parser: &mut CellParser<'de>, _: Self::Args) -> Result<Self, CellParserError<'de>> {
288 parser.unpack::<ConstU32<JETTON_BURN_TAG>>(())?;
289 Ok(Self {
290 query_id: parser.unpack(())?,
291 amount: parser.unpack_as::<_, VarInt<4>>(())?,
292 response_dst: parser.unpack(())?,
293 custom_payload: parser.parse_as::<_, Option<Ref<ParseFully>>>(NoArgs::EMPTY)?,
294 })
295 }
296}