defuse_nep413/
lib.rs

1use core::fmt::Display;
2
3use defuse_crypto::{Curve, Ed25519};
4use defuse_nep461::SignedMessageNep;
5use impl_tools::autoimpl;
6
7/// See [NEP-413](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
8#[cfg_attr(
9    feature = "borsh",
10    derive(::borsh::BorshSerialize, ::borsh::BorshDeserialize),
11    cfg_attr(feature = "abi", derive(::borsh::BorshSchema))
12)]
13#[cfg_attr(
14    feature = "serde",
15    ::cfg_eval::cfg_eval,
16    ::serde_with::serde_as,
17    derive(::serde::Serialize, ::serde::Deserialize),
18    cfg_attr(feature = "abi", derive(::schemars::JsonSchema)),
19    serde(rename_all = "camelCase")
20)]
21#[derive(Debug, Clone)]
22pub struct Nep413Payload {
23    pub message: String,
24
25    #[cfg_attr(feature = "serde", serde_as(as = "defuse_serde_utils::base64::Base64"))]
26    pub nonce: [u8; 32],
27
28    pub recipient: String,
29
30    #[cfg_attr(
31        feature = "serde",
32        serde(default, skip_serializing_if = "Option::is_none")
33    )]
34    pub callback_url: Option<String>,
35}
36
37impl SignedMessageNep for Nep413Payload {
38    const NEP_NUMBER: u32 = 413;
39}
40
41impl Nep413Payload {
42    #[inline]
43    pub fn new(message: String) -> Self {
44        Self {
45            message,
46            nonce: Default::default(),
47            recipient: String::new(),
48            callback_url: None,
49        }
50    }
51
52    #[must_use]
53    #[inline]
54    pub const fn with_nonce(mut self, nonce: [u8; 32]) -> Self {
55        self.nonce = nonce;
56        self
57    }
58
59    #[must_use]
60    #[inline]
61    pub fn with_recipient<S>(mut self, recipient: S) -> Self
62    where
63        S: Display,
64    {
65        self.recipient = recipient.to_string();
66        self
67    }
68
69    #[must_use]
70    #[inline]
71    pub fn with_callback_url(mut self, callback_url: String) -> Self {
72        self.callback_url = Some(callback_url);
73        self
74    }
75
76    #[cfg(feature = "borsh")]
77    #[inline]
78    pub fn prehash(&self) -> Vec<u8> {
79        use defuse_nep461::OffchainMessage;
80        borsh::to_vec(&(Self::OFFCHAIN_PREFIX_TAG, self)).expect("borsh")
81    }
82}
83
84#[cfg(all(any(feature = "near-contract", feature = "sha2"), feature = "borsh"))]
85impl defuse_crypto::Payload for Nep413Payload {
86    #[inline]
87    fn hash(&self) -> defuse_crypto::CryptoHash {
88        use defuse_digest::{Digest, Sha256};
89        Sha256::digest(self.prehash()).into()
90    }
91}
92
93#[cfg_attr(
94    feature = "serde",
95    ::cfg_eval::cfg_eval,
96    ::serde_with::serde_as,
97    derive(::serde::Serialize, ::serde::Deserialize),
98    cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
99)]
100#[autoimpl(Deref using self.payload)]
101#[derive(Debug, Clone)]
102pub struct SignedNep413Payload {
103    pub payload: Nep413Payload,
104
105    #[cfg_attr(
106        feature = "serde",
107        serde_as(as = "defuse_crypto::serde::AsCurve<Ed25519>")
108    )]
109    pub public_key: <Ed25519 as Curve>::PublicKey,
110    #[cfg_attr(
111        feature = "serde",
112        serde_as(as = "defuse_crypto::serde::AsCurve<Ed25519>")
113    )]
114    pub signature: <Ed25519 as Curve>::Signature,
115}
116
117#[cfg(all(any(feature = "near-contract", feature = "sha2"), feature = "borsh"))]
118impl defuse_crypto::Payload for SignedNep413Payload {
119    #[inline]
120    fn hash(&self) -> defuse_crypto::CryptoHash {
121        self.payload.hash()
122    }
123}
124
125#[cfg(feature = "near-contract")]
126impl defuse_crypto::SignedPayload for SignedNep413Payload {
127    type PublicKey = <Ed25519 as Curve>::PublicKey;
128
129    #[inline]
130    fn verify(&self) -> Option<Self::PublicKey> {
131        use defuse_crypto::{Payload, VerifiableCurve};
132        Ed25519::verify(&self.signature, &self.hash(), &self.public_key)
133    }
134}
135
136#[cfg(feature = "near-api")]
137const _: () = {
138    impl From<Nep413Payload> for near_api::signer::NEP413Payload {
139        fn from(payload: Nep413Payload) -> Self {
140            Self {
141                message: payload.message,
142                nonce: payload.nonce,
143                recipient: payload.recipient,
144                callback_url: payload.callback_url,
145            }
146        }
147    }
148};