1use async_trait::async_trait;
2use ethers::core::k256::ecdsa::SigningKey;
3use ethers::types::{H160, U256};
4use ethers_core::types::TransactionReceipt;
5use ethers_signers::Wallet;
6use eyre::eyre;
7use eyre::Result;
8use serde::de::DeserializeOwned;
9use std::time::Duration;
10
11use engine::Status;
12
13use crate::bindings::clearinghouse::Clearinghouse;
14use crate::bindings::withdraw_pool::WithdrawPool;
15use crate::builders::execute::deposit_collateral::DepositCollateralParams;
16use crate::builders::execute::slow_mode::SubmitSlowModeTxParams;
17use crate::eip712_structs::to_bytes12;
18use crate::engine::{ExecuteResponse, QueryV2};
19use crate::prelude::*;
20use crate::provider::VertexProvider;
21use crate::utils::client_error::{none_error, ClientError};
22use crate::utils::constants::SLOW_MODE_FEE;
23use crate::utils::deployment::Deployment;
24use crate::utils::deposit::{deposit_collateral, provider_with_signer};
25use crate::utils::rest::RestClient;
26use crate::utils::signer::wallet_with_chain_id;
27use crate::{engine, indexer, trigger};
28use crate::{extract_response_data, fields_to_vars};
29
30#[derive(Clone)]
31pub struct VertexClient {
32 pub client_mode: ClientMode,
33 pub deployment: Deployment,
34 pub client: RestClient,
35 pub gateway_url: String,
36 pub archive_url: String,
37 pub trigger_url: String,
38 pub subaccount_name_bytes: Option<[u8; 12]>,
39 pub book_addrs: Option<Vec<H160>>,
40 pub wallet: Option<Wallet<SigningKey>>,
41 pub chain_id: Option<U256>,
42 pub custom_node_url: Option<String>,
43}
44
45impl VertexClient {
46 pub fn new(client_mode: ClientMode) -> Self {
47 Self {
48 gateway_url: client_mode.default_gateway_url(),
49 archive_url: client_mode.default_archive_url(),
50 trigger_url: client_mode.default_trigger_url(),
51 client: RestClient::new(),
52 deployment: client_mode.deployment(),
53 book_addrs: None,
54 chain_id: None,
55 wallet: None,
56 subaccount_name_bytes: None,
57 custom_node_url: None,
58 client_mode,
59 }
60 }
61
62 pub fn with_gateway_url(&self, gateway_url: String) -> Self {
63 Self {
64 gateway_url,
65 ..self.clone()
66 }
67 }
68
69 pub fn with_archive_url(&self, archive_url: String) -> Self {
70 Self {
71 archive_url,
72 ..self.clone()
73 }
74 }
75
76 pub fn with_custom_node_url(&self, node_url: String) -> Self {
77 Self {
78 custom_node_url: Some(node_url),
79 ..self.clone()
80 }
81 }
82
83 pub fn with_trigger_url(&self, trigger_url: String) -> Self {
84 Self {
85 trigger_url,
86 ..self.clone()
87 }
88 }
89
90 pub async fn withdraw_pool(&self) -> Result<WithdrawPool<VertexProvider>> {
91 let provider = provider_with_signer(self)?;
92 let clearinghouse = Clearinghouse::new(self.deployment.clearinghouse, provider.clone());
93 let withdraw_pool_address = clearinghouse.get_withdraw_pool().call().await?;
94 let withdraw_pool = WithdrawPool::new(withdraw_pool_address, provider);
95 Ok(withdraw_pool)
96 }
97}
98
99#[async_trait]
100impl VertexBase for VertexClient {
101 async fn with_signer(&self, private_key: String) -> Result<Self> {
102 let contracts_response = self.get_contracts().await?;
103 let chain_id = U256::from(contracts_response.chain_id);
104 let wallet = wallet_with_chain_id(&private_key, chain_id)?;
105 let book_addrs: Vec<H160> = contracts_response
106 .book_addrs
107 .iter()
108 .map(H160::from)
109 .collect();
110 Ok(Self {
111 chain_id: Some(chain_id),
112 wallet: Some(wallet),
113 book_addrs: Some(book_addrs),
114 ..self.clone()
115 })
116 }
117
118 fn with_subaccount_name_bytes(&self, subaccount_name: [u8; 12]) -> Self {
119 Self {
120 subaccount_name_bytes: Some(subaccount_name),
121 ..self.clone()
122 }
123 }
124 fn wallet(&self) -> Result<&Wallet<SigningKey>> {
125 self.wallet.as_ref().ok_or(none_error("wallet"))
126 }
127
128 fn subaccount_name_bytes(&self) -> [u8; 12] {
129 self.subaccount_name_bytes.unwrap_or(to_bytes12("default"))
130 }
131
132 fn node_url(&self) -> String {
133 self.custom_node_url
134 .clone()
135 .unwrap_or(self.deployment.node_url.clone())
136 }
137
138 fn endpoint_addr(&self) -> H160 {
139 self.deployment.endpoint
140 }
141
142 fn querier_addr(&self) -> H160 {
143 self.deployment.querier
144 }
145
146 fn chain_id(&self) -> Result<U256> {
147 fields_to_vars!(self, chain_id);
148 Ok(chain_id)
149 }
150
151 fn book_addr(&self, product_id: u32) -> Result<H160> {
152 fields_to_vars!(self, (book_addrs: clone));
153 if product_id as usize >= book_addrs.len() {
154 return Err(eyre!(ClientError::ProductIdOutOfBounds(product_id)));
155 }
156 Ok(book_addrs[product_id as usize])
157 }
158
159 fn is_rest_client(&self) -> bool {
160 true
161 }
162}
163
164impl VertexBuilder for VertexClient {}
165
166#[async_trait]
167impl VertexExecute for VertexClient {
168 async fn execute(
169 &self,
170 execute: engine::Execute,
171 ) -> Result<Option<engine::ExecuteResponseData>> {
172 let url = format!("{}/execute", self.gateway_url);
173 let response: ExecuteResponse = self.client.post_request(&url, &execute).await?;
174 extract_response_data!(response, engine::ExecuteResponse => engine::ExecuteResponseData)
175 }
176
177 async fn execute_trigger(
178 &self,
179 execute: trigger::Execute,
180 ) -> Result<Option<engine::ExecuteResponseData>> {
181 let url = format!("{}/execute", self.trigger_url);
182 let response: ExecuteResponse = self.client.post_request(&url, &execute).await?;
183 extract_response_data!(response, engine::ExecuteResponse => engine::ExecuteResponseData)
184 }
185
186 async fn submit_slow_mode_tx(
187 &self,
188 params: SubmitSlowModeTxParams,
189 ) -> Result<Option<TransactionReceipt>> {
190 if params.mints_fee {
191 self.mint_mock_erc20(0, SLOW_MODE_FEE).await?;
192 if let Some(sleep_secs) = params.erc20_sleep_secs {
193 println!("sleeping for {}s (erc20)", sleep_secs);
194 tokio::time::sleep(Duration::from_secs(sleep_secs)).await;
195 }
196 }
197 if params.approves_fee {
198 self.approve_endpoint_allowance(0, SLOW_MODE_FEE).await?;
199 if let Some(sleep_secs) = params.erc20_sleep_secs {
200 println!("sleeping for {}s (erc20)", sleep_secs);
201 tokio::time::sleep(Duration::from_secs(sleep_secs)).await;
202 }
203 }
204 let endpoint = self.endpoint()?;
205 let mut tx = endpoint.submit_slow_mode_transaction(params.tx);
206 if let Some(gas_price) = params.gas_price {
207 tx = tx.gas_price(gas_price)
208 }
209 let tx_receipt = tx.send().await?.log_msg("pending tx").await?;
210 Ok(tx_receipt)
211 }
212
213 async fn deposit_collateral(
214 &self,
215 deposit_collateral_params: DepositCollateralParams,
216 ) -> Result<Option<TransactionReceipt>> {
217 deposit_collateral(self, deposit_collateral_params).await
218 }
219}
220
221#[async_trait]
222impl VertexQuery for VertexClient {
223 async fn query(&self, query: engine::Query) -> Result<engine::QueryResponseData> {
224 let url = format!("{}/query", self.gateway_url);
225 let response: engine::QueryResponse = self.client.post_request(&url, &query).await?;
226 let data =
227 extract_response_data!(response, engine::QueryResponse => engine::QueryResponseData)?;
228 Ok(data.unwrap())
229 }
230
231 async fn query_trigger(&self, query: trigger::Query) -> Result<trigger::QueryResponseData> {
232 let url = format!("{}/query", self.trigger_url);
233 let response: trigger::QueryResponse = self.client.post_request(&url, &query).await?;
234 let data =
235 extract_response_data!(response, trigger::QueryResponse => trigger::QueryResponseData)?;
236 Ok(data.unwrap())
237 }
238
239 async fn query_v2<R: DeserializeOwned + Send>(&self, path: &str, query: QueryV2) -> Result<R> {
240 let params = serde_url_params::to_string(&query)?;
241 let url = format!("{}/v2{path}?{params}", self.gateway_url.replace("/v1", ""));
242 self.client.get_request(&url).await
243 }
244}
245
246#[async_trait]
247impl VertexIndexer for VertexClient {
248 async fn query<R: DeserializeOwned + Send>(&self, query: indexer::Query) -> Result<R> {
249 self.client.post_request(&self.archive_url, &query).await
250 }
251
252 async fn query_v2<R: DeserializeOwned + Send>(
253 &self,
254 path: &str,
255 query: indexer::QueryV2,
256 ) -> Result<R> {
257 let params = serde_url_params::to_string(&query)?;
258 let url = format!("{}/v2{path}?{params}", self.archive_url.replace("/v1", ""));
259 self.client.get_request(&url).await
260 }
261}