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(all(
23 any(feature = "near-contract", feature = "sha2"),
24 any(feature = "binary", feature = "text")
25 ))]
26 pub fn create_payload_hash(
27 &self,
28 payload_prefix: &[u8],
29 payload: &[u8],
30 ) -> Result<defuse_crypto::CryptoHash, StringError> {
31 use defuse_digest::Digest;
32 let domain_len = u32::try_from(self.domain.len())
33 .map_err(|_| tlb_ton::Error::custom("domain: overflow"))?;
34 let payload_len = u32::try_from(payload.len())
35 .map_err(|_| tlb_ton::Error::custom("payload: overflow"))?;
36
37 let bytes = [
38 [0xff, 0xff].as_slice(),
39 b"ton-connect/sign-data/",
40 &self.address.workchain_id.to_be_bytes(),
41 self.address.address.as_ref(),
42 &domain_len.to_be_bytes(),
43 self.domain.as_bytes(),
44 &self.timestamp.to_be_bytes(),
45 payload_prefix,
46 &payload_len.to_be_bytes(),
47 payload,
48 ]
49 .concat();
50
51 Ok(defuse_digest::Sha256::digest(&bytes).into())
52 }
53}
54
55#[cfg(any(feature = "near-contract", feature = "sha2"))]
56pub trait PayloadSchema {
57 fn hash_with_context(
58 &self,
59 context: TonConnectPayloadContext,
60 ) -> Result<defuse_crypto::CryptoHash, StringError>;
61}
62
63#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
65#[cfg_attr(
66 feature = "serde",
67 derive(::serde::Serialize, ::serde::Deserialize),
68 cfg_attr(feature = "abi", derive(::schemars::JsonSchema)),
69 serde(tag = "type", rename_all = "snake_case")
70)]
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum TonConnectPayloadSchema {
73 #[cfg(feature = "text")]
74 Text(text::TextPayload),
75 #[cfg(feature = "binary")]
76 Binary(binary::BinaryPayload),
77 #[cfg(feature = "cell")]
78 Cell(cell::CellPayload),
79}
80
81impl TonConnectPayloadSchema {
82 #[cfg(feature = "text")]
83 pub fn text(txt: impl Into<String>) -> Self {
84 Self::Text(text::TextPayload { text: txt.into() })
85 }
86
87 #[cfg(feature = "binary")]
88 pub fn binary(bytes: impl Into<Vec<u8>>) -> Self {
89 Self::Binary(binary::BinaryPayload {
90 bytes: bytes.into(),
91 })
92 }
93
94 #[cfg(feature = "cell")]
95 pub const fn cell(schema_crc: u32, cell: tlb_ton::Cell) -> Self {
96 Self::Cell(cell::CellPayload { schema_crc, cell })
97 }
98}
99
100#[cfg(any(feature = "near-contract", feature = "sha2"))]
101impl PayloadSchema for TonConnectPayloadSchema {
102 fn hash_with_context(
103 &self,
104 context: TonConnectPayloadContext,
105 ) -> Result<defuse_crypto::CryptoHash, StringError> {
106 match self {
107 #[cfg(feature = "text")]
108 Self::Text(payload) => payload.hash_with_context(context),
109 #[cfg(feature = "binary")]
110 Self::Binary(payload) => payload.hash_with_context(context),
111 #[cfg(feature = "cell")]
112 Self::Cell(payload) => payload.hash_with_context(context),
113 }
114 }
115}