defuse_nep413/
lib.rs

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