defuse_core/payload/
ton_connect.rs

1use defuse_ton_connect::{SignedTonConnectPayload, TonConnectPayload, TonConnectPayloadSchema};
2use near_sdk::{
3    serde::de::{DeserializeOwned, Error},
4    serde_json,
5};
6
7use super::{DefusePayload, ExtractDefusePayload};
8
9impl<T> ExtractDefusePayload<T> for SignedTonConnectPayload
10where
11    T: DeserializeOwned,
12{
13    type Error = serde_json::Error;
14
15    #[inline]
16    fn extract_defuse_payload(self) -> Result<DefusePayload<T>, Self::Error> {
17        self.payload.extract_defuse_payload()
18    }
19}
20
21impl<T> ExtractDefusePayload<T> for TonConnectPayload
22where
23    T: DeserializeOwned,
24{
25    type Error = serde_json::Error;
26
27    fn extract_defuse_payload(self) -> Result<DefusePayload<T>, Self::Error> {
28        #[allow(irrefutable_let_patterns)]
29        let TonConnectPayloadSchema::Text(text) = self.payload else {
30            return Err(Error::custom("only text payload supported"));
31        };
32
33        let p: DefusePayload<T> = serde_json::from_str(&text)?;
34
35        // TON Connect [specification](https://docs.tonconsole.com/academy/sign-data#in-a-smart-contract-on-chain)
36        // requires to check that "timestamp is recent". We don't have fixed TTL
37        // for off-chain signatures but rather check if `deadline` is not expired.
38        //
39        // At first, we were asserting `(timestamp <= now())`, but that  was causing
40        // `simulate_intents()` to fail, since sometimes signed intent is simulated
41        // right after signing.
42        //
43        // So, we ended up to assert at least following:
44        if p.deadline.into_timestamp() < self.timestamp {
45            return Err(Error::custom("deadline < timestamp"));
46        }
47
48        Ok(p)
49    }
50}