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