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