tonlib_core/wallet/versioned/
v4.rs

1use crate::cell::{ArcCell, CellBuilder, CellParser, TonCellError};
2use crate::tlb_types::primitives::reference::Ref;
3use crate::tlb_types::tlb::TLB;
4use crate::types::TonHash;
5use crate::wallet::versioned::utils::write_up_to_4_msgs;
6
7/// WalletVersion::V4R1 | WalletVersion::V4R2
8#[derive(Debug, PartialEq, Clone)]
9pub struct WalletDataV4 {
10    pub seqno: u32,
11    pub wallet_id: i32,
12    pub public_key: TonHash,
13    pub plugins: Option<Ref<ArcCell>>,
14}
15
16/// https://docs.ton.org/participate/wallets/contracts#wallet-v4
17/// signature is not considered as part of msg body
18#[derive(Debug, PartialEq, Clone)]
19pub struct WalletExtMsgBodyV4 {
20    pub subwallet_id: i32,
21    pub valid_until: u32,
22    pub msg_seqno: u32,
23    pub opcode: u32,
24    pub msgs_modes: Vec<u8>,
25    pub msgs: Vec<ArcCell>,
26}
27
28impl WalletDataV4 {
29    pub fn new(wallet_id: i32, public_key: TonHash) -> Self {
30        Self {
31            seqno: 0,
32            wallet_id,
33            public_key,
34            plugins: None,
35        }
36    }
37}
38
39impl TLB for WalletDataV4 {
40    fn read_definition(parser: &mut CellParser) -> Result<Self, TonCellError> {
41        Ok(Self {
42            seqno: parser.load_u32(32)?,
43            wallet_id: parser.load_i32(32)?,
44            public_key: parser.load_tonhash()?,
45            plugins: TLB::read(parser)?,
46        })
47    }
48
49    fn write_definition(&self, dst: &mut CellBuilder) -> Result<(), TonCellError> {
50        dst.store_u32(32, self.seqno)?;
51        dst.store_i32(32, self.wallet_id)?;
52        dst.store_tonhash(&self.public_key)?;
53        self.plugins.write(dst)?;
54        Ok(())
55    }
56}
57
58impl TLB for WalletExtMsgBodyV4 {
59    fn read_definition(parser: &mut CellParser) -> Result<Self, TonCellError> {
60        let subwallet_id = parser.load_i32(32)?;
61        let valid_until = parser.load_u32(32)?;
62        let msg_seqno = parser.load_u32(32)?;
63        let opcode = parser.load_u32(8)?;
64        if opcode != 0 {
65            let err_str = format!("Unsupported opcode: {opcode}");
66            return Err(TonCellError::InternalError(err_str));
67        }
68
69        let msgs_cnt = parser.cell.references().len();
70        let mut msgs_modes = Vec::with_capacity(msgs_cnt);
71        let mut msgs = Vec::with_capacity(msgs_cnt);
72        for _ in 0..msgs_cnt {
73            msgs_modes.push(parser.load_u8(8)?);
74            msgs.push(parser.next_reference()?);
75        }
76        Ok(Self {
77            subwallet_id,
78            valid_until,
79            msg_seqno,
80            opcode,
81            msgs_modes,
82            msgs,
83        })
84    }
85
86    fn write_definition(&self, dst: &mut CellBuilder) -> Result<(), TonCellError> {
87        if self.opcode != 0 {
88            let err_str = format!("Unsupported opcode: {}", self.opcode);
89            return Err(TonCellError::InternalError(err_str));
90        }
91        dst.store_i32(32, self.subwallet_id)?;
92        dst.store_u32(32, self.valid_until)?;
93        dst.store_u32(32, self.msg_seqno)?;
94        dst.store_u32(8, self.opcode)?;
95        write_up_to_4_msgs(dst, &self.msgs, &self.msgs_modes)?;
96        Ok(())
97    }
98}
99
100#[cfg(test)]
101mod test {
102    use super::*;
103    use crate::cell::Cell;
104    use crate::tlb_types::tlb::TLB;
105    use crate::wallet::versioned::DEFAULT_WALLET_ID;
106
107    #[test]
108    fn test_wallet_data_v4() -> anyhow::Result<()> {
109        // https://tonviewer.com/UQCS65EGyiApUTLOYXDs4jOLoQNCE0o8oNnkmfIcm0iX5FRT
110        let src_boc_hex = "b5ee9c7241010101002b0000510000001429a9a317cbf377c9b73604c70bf73488ddceba14f763baef2ac70f68d1d6032a120149f440a6c9f37d";
111        let wallet_data = WalletDataV4::from_boc_hex(src_boc_hex)?;
112        assert_eq!(wallet_data.seqno, 20);
113        assert_eq!(wallet_data.wallet_id, DEFAULT_WALLET_ID);
114        assert_eq!(
115            wallet_data.public_key,
116            TonHash::from_hex("cbf377c9b73604c70bf73488ddceba14f763baef2ac70f68d1d6032a120149f4")?
117        );
118        assert_eq!(wallet_data.plugins, None);
119
120        let serial_boc_hex = wallet_data.to_boc_hex(false)?;
121        let restored = WalletDataV4::from_boc_hex(&serial_boc_hex)?;
122        assert_eq!(wallet_data, restored);
123        Ok(())
124    }
125
126    #[test]
127    fn test_wallet_ext_msg_body_v4() -> anyhow::Result<()> {
128        // https://tonviewer.com/transaction/891dbceffb986251768d4c33bb8dcf11d522408ff78b8e683d135304ca377b8b
129        let body_signed_cell = Cell::from_boc_hex("b5ee9c7201010201008700019c9dcd3a68926ad6fb9d094c5b72901bfc359ada50f22b648c6c2223c767135d397c7489c121071e45a5316a94a533d80c41450049ebeed406c419fea99117f40629a9a31767ad328900000013000301006842007847b4630eb08d9f486fe846d5496878556dfd5a084f82a9a3fb01224e67c84c200989680000000000000000000000000000")?;
130        let mut parser = body_signed_cell.parser();
131        parser.load_bytes(64)?; // signature
132        let body_cell = Cell::read(&mut parser)?;
133
134        let body = WalletExtMsgBodyV4::from_cell(&body_cell)?;
135        assert_eq!(body.subwallet_id, DEFAULT_WALLET_ID);
136        assert_eq!(body.valid_until, 1739403913);
137        assert_eq!(body.msg_seqno, 19);
138        assert_eq!(body.opcode, 0);
139        assert_eq!(body.msgs_modes, vec![3]);
140        assert_eq!(body.msgs.len(), 1);
141
142        let serial_cell = body.to_cell()?;
143        assert_eq!(body_cell, serial_cell);
144        Ok(())
145    }
146}