use async_trait::async_trait;
use ethers::core::k256::ecdsa::SigningKey;
use ethers::types::{H160, U256};
use ethers_core::types::TransactionReceipt;
use ethers_signers::Wallet;
use eyre::eyre;
use eyre::Result;
use serde::de::DeserializeOwned;
use std::time::Duration;
use engine::Status;
use crate::bindings::clearinghouse::Clearinghouse;
use crate::bindings::withdraw_pool::WithdrawPool;
use crate::builders::execute::deposit_collateral::DepositCollateralParams;
use crate::builders::execute::slow_mode::SubmitSlowModeTxParams;
use crate::eip712_structs::to_bytes12;
use crate::engine::{ExecuteResponse, QueryV2};
use crate::prelude::*;
use crate::provider::NadoProvider;
use crate::utils::client_error::none_error;
use crate::utils::constants::SLOW_MODE_FEE;
use crate::utils::deployment::Deployment;
use crate::utils::deposit::{deposit_collateral, provider_with_signer};
use crate::utils::rest::RestClient;
use crate::utils::signer::wallet_with_chain_id;
use crate::{engine, indexer, trigger};
use crate::{extract_response_data, fields_to_vars};
#[derive(Clone)]
pub struct NadoClient {
pub client_mode: ClientMode,
pub deployment: Deployment,
pub client: RestClient,
pub gateway_url: String,
pub archive_url: String,
pub trigger_url: String,
pub subaccount_name_bytes: Option<[u8; 12]>,
pub wallet: Option<Wallet<SigningKey>>,
pub chain_id: Option<U256>,
pub custom_node_url: Option<String>,
}
impl NadoClient {
pub fn new(client_mode: ClientMode) -> Self {
Self {
gateway_url: client_mode.default_gateway_url(),
archive_url: client_mode.default_archive_url(),
trigger_url: client_mode.default_trigger_url(),
client: RestClient::new(),
deployment: client_mode.deployment(),
chain_id: None,
wallet: None,
subaccount_name_bytes: None,
custom_node_url: None,
client_mode,
}
}
pub fn with_gateway_url(&self, gateway_url: String) -> Self {
Self {
gateway_url,
..self.clone()
}
}
pub fn with_archive_url(&self, archive_url: String) -> Self {
Self {
archive_url,
..self.clone()
}
}
pub fn with_custom_node_url(&self, node_url: String) -> Self {
Self {
custom_node_url: Some(node_url),
..self.clone()
}
}
pub fn with_trigger_url(&self, trigger_url: String) -> Self {
Self {
trigger_url,
..self.clone()
}
}
pub async fn withdraw_pool(&self) -> Result<WithdrawPool<NadoProvider>> {
let provider = provider_with_signer(self)?;
let clearinghouse = Clearinghouse::new(self.deployment.clearinghouse, provider.clone());
let withdraw_pool_address = clearinghouse.get_withdraw_pool().call().await?;
let withdraw_pool = WithdrawPool::new(withdraw_pool_address, provider);
Ok(withdraw_pool)
}
}
#[async_trait]
impl NadoBase for NadoClient {
async fn with_signer(&self, private_key: String) -> Result<Self> {
let contracts_response = self.get_contracts().await?;
let chain_id = U256::from(contracts_response.chain_id);
let wallet = wallet_with_chain_id(&private_key, chain_id)?;
Ok(Self {
chain_id: Some(chain_id),
wallet: Some(wallet),
..self.clone()
})
}
fn with_subaccount_name_bytes(&self, subaccount_name: [u8; 12]) -> Self {
Self {
subaccount_name_bytes: Some(subaccount_name),
..self.clone()
}
}
fn wallet(&self) -> Result<&Wallet<SigningKey>> {
self.wallet.as_ref().ok_or(none_error("wallet"))
}
fn subaccount_name_bytes(&self) -> [u8; 12] {
self.subaccount_name_bytes.unwrap_or(to_bytes12("default"))
}
fn node_url(&self) -> String {
self.custom_node_url
.clone()
.unwrap_or(self.deployment.node_url.clone())
}
fn endpoint_addr(&self) -> H160 {
self.deployment.endpoint
}
fn querier_addr(&self) -> H160 {
self.deployment.querier
}
fn chain_id(&self) -> Result<U256> {
fields_to_vars!(self, chain_id);
Ok(chain_id)
}
fn is_rest_client(&self) -> bool {
true
}
}
impl NadoBuilder for NadoClient {}
#[async_trait]
impl NadoExecute for NadoClient {
async fn execute(
&self,
execute: engine::Execute,
) -> Result<Option<engine::ExecuteResponseData>> {
let url = format!("{}/execute", self.gateway_url);
let response: ExecuteResponse = self.client.post_request(&url, &execute).await?;
extract_response_data!(response, engine::ExecuteResponse => engine::ExecuteResponseData)
}
async fn execute_trigger(
&self,
execute: trigger::Execute,
) -> Result<Option<engine::ExecuteResponseData>> {
let url = format!("{}/execute", self.trigger_url);
let response: ExecuteResponse = self.client.post_request(&url, &execute).await?;
extract_response_data!(response, engine::ExecuteResponse => engine::ExecuteResponseData)
}
async fn submit_slow_mode_tx(
&self,
params: SubmitSlowModeTxParams,
) -> Result<Option<TransactionReceipt>> {
if params.mints_fee {
self.mint_mock_erc20(0, SLOW_MODE_FEE).await?;
if let Some(sleep_secs) = params.erc20_sleep_secs {
println!("sleeping for {sleep_secs}s (erc20)");
tokio::time::sleep(Duration::from_secs(sleep_secs)).await;
}
}
if params.approves_fee {
self.approve_endpoint_allowance(0, SLOW_MODE_FEE).await?;
if let Some(sleep_secs) = params.erc20_sleep_secs {
println!("sleeping for {sleep_secs}s (erc20)");
tokio::time::sleep(Duration::from_secs(sleep_secs)).await;
}
}
let endpoint = self.endpoint()?;
let mut tx = endpoint.submit_slow_mode_transaction(params.tx);
if let Some(gas_price) = params.gas_price {
tx = tx.gas_price(gas_price)
}
let tx_receipt = tx.send().await?.log_msg("pending tx").await?;
Ok(tx_receipt)
}
async fn deposit_collateral(
&self,
deposit_collateral_params: DepositCollateralParams,
) -> Result<Option<TransactionReceipt>> {
deposit_collateral(self, deposit_collateral_params).await
}
}
#[async_trait]
impl NadoQuery for NadoClient {
async fn query(&self, query: engine::Query) -> Result<engine::QueryResponseData> {
let url = format!("{}/query", self.gateway_url);
let response: engine::QueryResponse = self.client.post_request(&url, &query).await?;
let data =
extract_response_data!(response, engine::QueryResponse => engine::QueryResponseData)?;
Ok(data.unwrap())
}
async fn query_trigger(&self, query: trigger::Query) -> Result<trigger::QueryResponseData> {
let url = format!("{}/query", self.trigger_url);
let response: trigger::QueryResponse = self.client.post_request(&url, &query).await?;
let data =
extract_response_data!(response, trigger::QueryResponse => trigger::QueryResponseData)?;
Ok(data.unwrap())
}
async fn query_v2<R: DeserializeOwned + Send>(&self, path: &str, query: QueryV2) -> Result<R> {
let params = serde_url_params::to_string(&query)?;
let url = format!("{}/v2{path}?{params}", self.gateway_url.replace("/v1", ""));
self.client.get_request(&url).await
}
}
#[async_trait]
impl NadoIndexer for NadoClient {
async fn query<R: DeserializeOwned + Send>(&self, query: indexer::Query) -> Result<R> {
self.client.post_request(&self.archive_url, &query).await
}
async fn query_v2<R: DeserializeOwned + Send>(
&self,
path: &str,
query: indexer::QueryV2,
) -> Result<R> {
let params = serde_url_params::to_string(&query)?;
let url = format!("{}/v2{path}?{params}", self.archive_url.replace("/v1", ""));
self.client.get_request(&url).await
}
}