Skip to main content

magiclink_server_wallets_api/
core_api.rs

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