defuse_ton_connect/schema/
mod.rs1use core::str;
2use std::borrow::Cow;
3use std::fmt::Debug;
4
5use tlb_ton::{MsgAddress, StringError};
6
7#[cfg(feature = "binary")]
8mod binary;
9#[cfg(feature = "cell")]
10mod cell;
11#[cfg(feature = "text")]
12mod text;
13
14pub struct TonConnectPayloadContext<'a> {
15 pub address: MsgAddress,
16 pub domain: Cow<'a, str>,
17 pub timestamp: u64,
18}
19
20impl TonConnectPayloadContext<'_> {
21 #[cfg(any(feature = "binary", feature = "text"))]
23 pub fn create_payload_hash(
24 &self,
25 payload_prefix: &[u8],
26 payload: &[u8],
27 ) -> Result<defuse_crypto::CryptoHash, StringError> {
28 use defuse_digest::{Digest, sha2::Sha256};
29
30 let domain_len = u32::try_from(self.domain.len())
31 .map_err(|_| tlb_ton::Error::custom("domain: overflow"))?;
32 let payload_len = u32::try_from(payload.len())
33 .map_err(|_| tlb_ton::Error::custom("payload: overflow"))?;
34
35 Ok(Sha256::new_with_prefix(b"\xFF\xFFton-connect/sign-data/")
36 .chain_update(self.address.workchain_id.to_be_bytes())
37 .chain_update(self.address.address)
38 .chain_update(domain_len.to_be_bytes())
39 .chain_update(self.domain.as_bytes())
40 .chain_update(self.timestamp.to_be_bytes())
41 .chain_update(payload_prefix)
42 .chain_update(payload_len.to_be_bytes())
43 .chain_update(payload)
44 .finalize()
45 .into())
46 }
47}
48
49pub trait PayloadSchema {
50 fn hash_with_context(
51 &self,
52 context: TonConnectPayloadContext,
53 ) -> Result<defuse_crypto::CryptoHash, StringError>;
54}
55
56#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
58#[cfg_attr(
59 feature = "serde",
60 derive(::serde::Serialize, ::serde::Deserialize),
61 cfg_attr(feature = "abi", derive(::schemars::JsonSchema)),
62 serde(tag = "type", rename_all = "snake_case")
63)]
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum TonConnectPayloadSchema {
66 #[cfg(feature = "text")]
67 Text(text::TextPayload),
68 #[cfg(feature = "binary")]
69 Binary(binary::BinaryPayload),
70 #[cfg(feature = "cell")]
71 Cell(cell::CellPayload),
72}
73
74impl TonConnectPayloadSchema {
75 #[cfg(feature = "text")]
76 pub fn text(txt: impl Into<String>) -> Self {
77 Self::Text(text::TextPayload { text: txt.into() })
78 }
79
80 #[cfg(feature = "binary")]
81 pub fn binary(bytes: impl Into<Vec<u8>>) -> Self {
82 Self::Binary(binary::BinaryPayload {
83 bytes: bytes.into(),
84 })
85 }
86
87 #[cfg(feature = "cell")]
88 pub const fn cell(schema_crc: u32, cell: tlb_ton::Cell) -> Self {
89 Self::Cell(cell::CellPayload { schema_crc, cell })
90 }
91}
92
93impl PayloadSchema for TonConnectPayloadSchema {
94 fn hash_with_context(
95 &self,
96 context: TonConnectPayloadContext,
97 ) -> Result<defuse_crypto::CryptoHash, StringError> {
98 match self {
99 #[cfg(feature = "text")]
100 Self::Text(payload) => payload.hash_with_context(context),
101 #[cfg(feature = "binary")]
102 Self::Binary(payload) => payload.hash_with_context(context),
103 #[cfg(feature = "cell")]
104 Self::Cell(payload) => payload.hash_with_context(context),
105 }
106 }
107}