magiclink_server_wallets_api/
core_api.rs1use crate::{
2 domain::{
3 ApiResponse, MessageSigning, Signing, SolanaSignMessage, Wallet, WalletGroup
4 },
5 dto::{
6 BitcoinPayload,
7 EVMPayload,
8 MessageSigningParam,
9 RecoveryParam,
10 SolanaPayload,
11 SolanaSignMessageParam,
12 TransSigningParam,
13 WalletGroupMetadataParam,
14 WalletParam,
15 },
16};
17use reqwest::header::{
18 HeaderMap,
19 HeaderValue,
20};
21use reqwest_middleware::{
22 ClientBuilder,
23 ClientWithMiddleware,
24 Error,
25};
26use reqwest_tracing::TracingMiddleware;
27use std::{
28 env,
29 future::Future,
30};
31
32static BASE_URI: &str = "https://tee.magiclabs.com/v1/api";
33
34pub struct CoreApi {
35 client: ClientWithMiddleware,
36 api_secret: String,
37}
38
39impl Default for CoreApi {
40 fn default() -> Self {
41 dotenv::dotenv().ok();
42
43 let client: ClientWithMiddleware = ClientBuilder::new(reqwest::Client::new())
44 .with(TracingMiddleware::default())
45 .build();
46
47 let api_secret = env::var("MAGIC_LINK_API_SECRET").expect("MAGIC_LINK_API_SECRET is missing");
48
49 Self { client, api_secret }
50 }
51}
52
53impl CoreApi {
54 pub fn new(client: ClientWithMiddleware, api_secret: String) -> Self {
55 Self { client, api_secret }
56 }
57
58 fn default_headers(&self) -> HeaderMap {
59 let mut headers = HeaderMap::new();
60 headers.append(
61 "content-type",
62 HeaderValue::from_str("application/json").expect("Expect applicaton/json as content-type"),
63 );
64 headers.append(
65 "x-magic-secret-key",
66 HeaderValue::from_str(self.api_secret.as_str()).expect("Set api secret to header"),
67 );
68 headers
69 }
70}
71
72pub trait WalletApi {
73 fn create_wallet_group(
74 &self,
75 metadata: &WalletGroupMetadataParam,
76 ) -> impl Future<Output = Result<ApiResponse<WalletGroup>, Error>>;
77 fn wallet_group_list(&self) -> impl Future<Output = Result<ApiResponse<Vec<WalletGroup>>, Error>>;
78 fn create_wallet(&self, wallet_param: &WalletParam) -> impl Future<Output = Result<ApiResponse<Wallet>, Error>>;
79 fn recover_wallet(&self, recovery_param: &RecoveryParam) -> impl Future<Output = Result<ApiResponse<Wallet>, Error>>;
80}
81
82pub trait SigningApi {
83 fn evm_transaction_signing(
84 &self,
85 trans_sign_param: &TransSigningParam<EVMPayload>,
86 ) -> impl Future<Output = Result<ApiResponse<Signing>, Error>>;
87 fn solana_transaction_signing(
88 &self,
89 trans_sign_param: &TransSigningParam<SolanaPayload>,
90 ) -> impl Future<Output = Result<ApiResponse<Signing>, Error>>;
91 fn bitcoin_transaction_signing(
92 &self,
93 trans_sign_param: &TransSigningParam<BitcoinPayload>,
94 ) -> impl Future<Output = Result<ApiResponse<Signing>, Error>>;
95
96 fn message_signing(
97 &self,
98 message_sign_param: &MessageSigningParam,
99 ) -> impl Future<Output = Result<ApiResponse<MessageSigning>, Error>>;
100 fn solana_message_signing(
101 &self,
102 solana_sign_message_param: &SolanaSignMessageParam,
103 ) -> impl Future<Output = Result<ApiResponse<SolanaSignMessage>, Error>>;
104}
105
106impl WalletApi for CoreApi {
107 async fn create_wallet_group(&self, metadata: &WalletGroupMetadataParam) -> Result<ApiResponse<WalletGroup>, Error> {
108 let url: String = format!("{}/wallet_group", BASE_URI);
109 self
110 .client
111 .post(url)
112 .headers(self.default_headers())
113 .json(metadata)
114 .send()
115 .await?
116 .json::<ApiResponse<WalletGroup>>()
117 .await
118 .map_err(|e| Error::Reqwest(e))
119 }
120
121 async fn wallet_group_list(&self) -> Result<ApiResponse<Vec<WalletGroup>>, Error> {
122 let url: String = format!("{}/wallet_groups", BASE_URI);
123 self
124 .client
125 .get(url)
126 .headers(self.default_headers())
127 .send()
128 .await?
129 .json::<ApiResponse<Vec<WalletGroup>>>()
130 .await
131 .map_err(|e| Error::Reqwest(e))
132 }
133
134 async fn create_wallet(&self, wallet_param: &WalletParam) -> Result<ApiResponse<Wallet>, Error> {
135 let url: String = format!("{}/wallet", BASE_URI);
136 self
137 .client
138 .post(url)
139 .headers(self.default_headers())
140 .json(wallet_param)
141 .send()
142 .await?
143 .json::<ApiResponse<Wallet>>()
144 .await
145 .map_err(|e| Error::Reqwest(e))
146 }
147
148 async fn recover_wallet(&self, recovery_param: &RecoveryParam) -> Result<ApiResponse<Wallet>, Error> {
149 let url: String = format!("{}/wallet/recovery/confirm", BASE_URI);
150 self
151 .client
152 .post(url)
153 .headers(self.default_headers())
154 .json(recovery_param)
155 .send()
156 .await?
157 .json::<ApiResponse<Wallet>>()
158 .await
159 .map_err(|e| Error::Reqwest(e))
160 }
161}
162
163impl SigningApi for CoreApi {
164 async fn evm_transaction_signing(
165 &self,
166 evm_sign_param: &TransSigningParam<EVMPayload>,
167 ) -> Result<ApiResponse<Signing>, Error> {
168 let url = format!("{}/wallet/sign_transaction", BASE_URI);
169 self
170 .client
171 .post(url)
172 .headers(self.default_headers())
173 .json(evm_sign_param)
174 .send()
175 .await?
176 .json::<ApiResponse<Signing>>()
177 .await
178 .map_err(|e| Error::Reqwest(e))
179 }
180
181 async fn solana_transaction_signing(
182 &self,
183 solana_sign_param: &TransSigningParam<SolanaPayload>,
184 ) -> Result<ApiResponse<Signing>, Error> {
185 let url = format!("{}/wallet/sign_transaction", BASE_URI);
186 self
187 .client
188 .post(url)
189 .headers(self.default_headers())
190 .json(solana_sign_param)
191 .send()
192 .await?
193 .json::<ApiResponse<Signing>>()
194 .await
195 .map_err(|e| Error::Reqwest(e))
196 }
197
198 async fn bitcoin_transaction_signing(
199 &self,
200 bitcoin_sign_param: &TransSigningParam<BitcoinPayload>,
201 ) -> Result<ApiResponse<Signing>, Error> {
202 let url = format!("{}/wallet/sign_transaction", BASE_URI);
203 self
204 .client
205 .post(url)
206 .headers(self.default_headers())
207 .json(bitcoin_sign_param)
208 .send()
209 .await?
210 .json::<ApiResponse<Signing>>()
211 .await
212 .map_err(|e| Error::Reqwest(e))
213 }
214
215 async fn message_signing(
216 &self,
217 message_sign_param: &MessageSigningParam,
218 ) -> Result<ApiResponse<MessageSigning>, Error> {
219 let url = format!("{}/wallet/sign_data", BASE_URI);
220 self
221 .client
222 .post(url)
223 .headers(self.default_headers())
224 .json(message_sign_param)
225 .send()
226 .await?
227 .json::<ApiResponse<MessageSigning>>()
228 .await
229 .map_err(|e| Error::Reqwest(e))
230 }
231
232 async fn solana_message_signing(
233 &self,
234 solana_sign_message_param: &SolanaSignMessageParam,
235 ) -> Result<ApiResponse<SolanaSignMessage>, Error> {
236 let url = format!("{}/wallet/svm/sign_message", BASE_URI);
237 self
238 .client
239 .post(url)
240 .headers(self.default_headers())
241 .json(solana_sign_message_param)
242 .send()
243 .await?
244 .json::<ApiResponse<SolanaSignMessage>>()
245 .await
246 .map_err(|e| Error::Reqwest(e))
247 }
248}