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#[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};