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        let TonConnectPayloadSchema::Text { text } = self.payload else {
29            return Err(Error::custom("only text payload supported"));
30        };
31
32        let p: DefusePayload<T> = serde_json::from_str(&text)?;
33
34        // TON Connect [specification](https://docs.tonconsole.com/academy/sign-data#in-a-smart-contract-on-chain)
35        // requires to check that "timestamp is recent". We don't have fixed TTL
36        // for off-chain signatures but rather check if `deadline` is not expired.
37        //
38        // At first, we were asserting `(timestamp <= now())`, but that  was causing
39        // `simulate_intents()` to fail, since sometimes signed intent is simulated
40        // right after signing.
41        //
42        // So, we ended up to assert at least following:
43        if p.deadline.into_timestamp() < self.timestamp {
44            return Err(Error::custom("deadline < timestamp"));
45        }
46
47        Ok(p)
48    }
49}