Skip to main content

magiclink_server_wallets_api/
core_api.rs

1use 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}