Skip to main content

ton_contracts/jetton/
wallet.rs

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/// Jetton Transfer message from [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#tl-b-schema)
17/// ```tlb
18/// transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
19/// response_destination:MsgAddress custom_payload:(Maybe ^Cell)
20/// forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
21/// = InternalMsgBody;
22/// ```
23#[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            // transfer#0f8a7ea5
47            .pack(JETTON_TRANSFER_TAG, ())?
48            // query_id:uint64
49            .pack(self.query_id, ())?
50            // amount:(VarUInteger 16)
51            .pack_as::<_, &VarInt<4>>(&self.amount, ())?
52            // destination:MsgAddress
53            .pack(self.dst, ())?
54            // response_destination:MsgAddress
55            .pack(self.response_dst, ())?
56            // custom_payload:(Maybe ^Cell)
57            .store_as::<_, Option<Ref>>(self.custom_payload.as_ref(), NoArgs::EMPTY)?
58            // forward_ton_amount:(VarUInteger 16)
59            .pack_as::<_, &VarInt<4>>(&self.forward_ton_amount, ())?
60            // forward_payload:(Either Cell ^Cell)
61            .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        // transfer#0f8a7ea5
76        parser.unpack::<ConstU32<JETTON_TRANSFER_TAG>>(())?;
77        Ok(Self {
78            // query_id:uint64
79            query_id: parser.unpack(())?,
80            // amount:(VarUInteger 16)
81            amount: parser.unpack_as::<_, VarInt<4>>(())?,
82            // destination:MsgAddress
83            dst: parser.unpack(())?,
84            // response_destination:MsgAddress
85            response_dst: parser.unpack(())?,
86            // custom_payload:(Maybe ^Cell)
87            custom_payload: parser.parse_as::<_, Option<Ref<ParseFully>>>(P::Args::EMPTY)?,
88            // forward_ton_amount:(VarUInteger 16)
89            forward_ton_amount: parser.unpack_as::<_, VarInt<4>>(())?,
90            // forward_payload:(Either Cell ^Cell)
91            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            // clone, so we don't advance original parser
134            && parser.clone().unpack::<u32>(())? == Self::COMMENT_PREFIX
135        {
136            // skip the prefix
137            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/// Jetton Transfer Notification message from[TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#tl-b-schema)
193/// ```tlb
194/// transfer_notification#7362d09c query_id:uint64 amount:(VarUInteger 16)
195/// sender:MsgAddress forward_payload:(Either Cell ^Cell)
196/// = InternalMsgBody;
197/// ```
198#[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/// Jetton Burn message from [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#tl-b-schema)
249/// ```tlb
250/// burn#595f07bc query_id:uint64 amount:(VarUInteger 16)
251/// response_destination:MsgAddress custom_payload:(Maybe ^Cell)
252/// = InternalMsgBody;
253/// ```
254#[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}