1use core::fmt::Display;
2
3use defuse_crypto::{Curve, Ed25519};
4use defuse_nep461::SignedMessageNep;
5use impl_tools::autoimpl;
6
7#[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};