Skip to content

Commit

Permalink
chore: wip prepare_detailed()
Browse files Browse the repository at this point in the history
  • Loading branch information
chris13524 committed Dec 18, 2024
1 parent 0a5c058 commit bd780a8
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 64 deletions.
4 changes: 2 additions & 2 deletions crates/kotlin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use {
account_client::AccountClient as YAccountClient,
chain_abstraction::{
api::{
prepare::{PrepareResponse, RouteResponseAvailable},
prepare::{PrepareResponse, PrepareResponseAvailable},
status::{StatusResponse, StatusResponseCompleted},
InitialTransaction,
},
Expand Down Expand Up @@ -141,7 +141,7 @@ impl ChainAbstractionClient {

pub async fn get_ui_fields(
&self,
route_response: RouteResponseAvailable,
route_response: PrepareResponseAvailable,
currency: Currency,
) -> Result<UiFields, FFIError> {
self.client
Expand Down
22 changes: 11 additions & 11 deletions crates/yttrium/src/chain_abstraction/api/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl FundingMetadata {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
#[serde(rename_all = "camelCase")]
pub struct RouteResponseAvailable {
pub struct PrepareResponseAvailable {
pub orchestration_id: String,
pub initial_transaction: Transaction,
pub transactions: Vec<Transaction>,
Expand All @@ -106,21 +106,21 @@ pub struct RouteResponseAvailable {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
#[serde(rename_all = "camelCase")]
pub struct RouteResponseNotRequired {
pub struct PrepareResponseNotRequired {
pub initial_transaction: Transaction,
pub transactions: Vec<Transaction>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Enum))]
#[serde(untagged)]
pub enum RouteResponseSuccess {
Available(RouteResponseAvailable),
NotRequired(RouteResponseNotRequired),
pub enum PrepareResponseSuccess {
Available(PrepareResponseAvailable),
NotRequired(PrepareResponseNotRequired),
}

impl RouteResponseSuccess {
pub fn into_option(self) -> Option<RouteResponseAvailable> {
impl PrepareResponseSuccess {
pub fn into_option(self) -> Option<PrepareResponseAvailable> {
match self {
Self::Available(a) => Some(a),
Self::NotRequired(_) => None,
Expand All @@ -132,7 +132,7 @@ impl RouteResponseSuccess {
/// response
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
pub struct RouteResponseError {
pub struct PrepareResponseError {
pub error: BridgingError,
}

Expand All @@ -149,14 +149,14 @@ pub enum BridgingError {
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Enum))]
#[serde(untagged)]
pub enum PrepareResponse {
Success(RouteResponseSuccess),
Error(RouteResponseError),
Success(PrepareResponseSuccess),
Error(PrepareResponseError),
}

impl PrepareResponse {
pub fn into_result(
self,
) -> Result<RouteResponseSuccess, RouteResponseError> {
) -> Result<PrepareResponseSuccess, PrepareResponseError> {
match self {
Self::Success(success) => Ok(success),
Self::Error(error) => Err(error),
Expand Down
105 changes: 74 additions & 31 deletions crates/yttrium/src/chain_abstraction/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use {
FUNGIBLE_PRICE_ENDPOINT_PATH, NATIVE_TOKEN_ADDRESS,
},
prepare::{
PrepareRequest, PrepareResponse, RouteQueryParams,
RouteResponseAvailable, ROUTE_ENDPOINT_PATH,
PrepareRequest, PrepareResponse, PrepareResponseAvailable,
PrepareResponseSuccess, RouteQueryParams, ROUTE_ENDPOINT_PATH,
},
status::{
StatusQueryParams, StatusResponse, StatusResponseCompleted,
Expand All @@ -16,12 +16,15 @@ use {
InitialTransaction, Transaction,
},
currency::Currency,
error::{RouteError, WaitForSuccessError},
error::{
PrepareDetailedError, PrepareDetailedResponse,
PrepareDetailedResponseSuccess, PrepareError, WaitForSuccessError,
},
ui_fields::UiFields,
},
crate::{
chain_abstraction::{
error::RouteUiFieldsError, l1_data_fee::get_l1_data_fee, ui_fields,
error::UiFieldsError, l1_data_fee::get_l1_data_fee, ui_fields,
},
erc20::ERC20,
},
Expand Down Expand Up @@ -65,41 +68,41 @@ impl Client {
pub async fn prepare(
&self,
transaction: InitialTransaction,
) -> Result<PrepareResponse, RouteError> {
) -> Result<PrepareResponse, PrepareError> {
let response = self
.client
.post(self.base_url.join(ROUTE_ENDPOINT_PATH).unwrap())
.json(&PrepareRequest { transaction })
.query(&RouteQueryParams { project_id: self.project_id.clone() })
.send()
.await
.map_err(RouteError::Request)?;
.map_err(PrepareError::Request)?;
let status = response.status();
if status.is_success() {
let text =
response.text().await.map_err(RouteError::DecodingText)?;
response.text().await.map_err(PrepareError::DecodingText)?;
serde_json::from_str(&text)
.map_err(|e| RouteError::DecodingJson(e, text))
.map_err(|e| PrepareError::DecodingJson(e, text))
} else {
Err(RouteError::RequestFailed(response.text().await))
Err(PrepareError::RequestFailed(response.text().await))
}
}

pub async fn get_ui_fields(
&self,
route_response: RouteResponseAvailable,
prepare_response: PrepareResponseAvailable,
local_currency: Currency,
// TODO use this to e.g. modify priority fee
// _speed: String,
) -> Result<UiFields, RouteUiFieldsError> {
) -> Result<UiFields, UiFieldsError> {
if local_currency != Currency::Usd {
unimplemented!("Only USD currency is supported for now");
}

let chains = route_response
let chains = prepare_response
.transactions
.iter()
.chain(std::iter::once(&route_response.initial_transaction))
.chain(std::iter::once(&prepare_response.initial_transaction))
.map(|t| t.chain_id.clone())
.collect::<HashSet<_>>();
println!("chains: {chains:?}");
Expand All @@ -111,7 +114,7 @@ impl Client {
.iter()
.map(|t| format!("{}:{}", t, NATIVE_TOKEN_ADDRESS))
.chain(
route_response.metadata.funding_from.iter().map(|f| {
prepare_response.metadata.funding_from.iter().map(|f| {
format!("{}:{}", f.chain_id, f.token_contract)
}),
)
Expand All @@ -135,14 +138,14 @@ impl Client {
})
.send()
.await
.map_err(RouteUiFieldsError::Request)?;
.map_err(UiFieldsError::Request)?;
let prices = if response.status().is_success() {
response
.json::<PriceResponseBody>()
.await
.map_err(RouteUiFieldsError::Json)
.map_err(UiFieldsError::Json)
} else {
Err(RouteUiFieldsError::RequestFailed(
Err(UiFieldsError::RequestFailed(
response.status(),
response.text().await,
))
Expand All @@ -166,7 +169,7 @@ impl Client {
async fn l1_data_fee(
txn: Transaction,
providers: &Client,
) -> Result<U256, RouteUiFieldsError> {
) -> Result<U256, UiFieldsError> {
Ok(get_l1_data_fee(
TransactionRequest::default()
.with_from(txn.from)
Expand All @@ -191,13 +194,13 @@ impl Client {
}

let route_l1_data_fee_futures = futures::future::try_join_all(
route_response
prepare_response
.transactions
.iter()
.map(|txn| l1_data_fee(txn.clone(), self)),
);
let initial_l1_data_fee_future =
l1_data_fee(route_response.initial_transaction.clone(), self);
l1_data_fee(prepare_response.initial_transaction.clone(), self);

let (fungibles, eip1559_fees, route_l1_data_fees, initial_l1_data_fee) =
tokio::try_join!(
Expand Down Expand Up @@ -225,8 +228,8 @@ impl Client {
}

let mut estimated_transactions =
Vec::with_capacity(route_response.transactions.len());
for (txn, l1_data_fee) in route_response
Vec::with_capacity(prepare_response.transactions.len());
for (txn, l1_data_fee) in prepare_response
.clone()
.transactions
.into_iter()
Expand All @@ -239,23 +242,55 @@ impl Client {
));
}
let estimated_initial_transaction = estimate_gas_fees(
route_response.initial_transaction.clone(),
prepare_response.initial_transaction.clone(),
&eip1559_fees,
initial_l1_data_fee,
);

Ok(ui_fields::ui_fields(
route_response,
prepare_response,
estimated_transactions,
estimated_initial_transaction,
fungibles,
))
}

// TODO test
pub async fn prepare_detailed(
&self,
transaction: InitialTransaction,
local_currency: Currency,
// TODO use this to e.g. modify priority fee
// _speed: String,
) -> Result<PrepareDetailedResponse, PrepareDetailedError> {
let response = self
.prepare(transaction)
.await
.map_err(PrepareDetailedError::Prepare)?;
match response {
PrepareResponse::Success(response) => {
Ok(PrepareDetailedResponse::Success(match response {
PrepareResponseSuccess::Available(response) => {
let res = self
.get_ui_fields(response, local_currency)
.await
.map_err(PrepareDetailedError::UiFields)?;
PrepareDetailedResponseSuccess::Available(res)
}
PrepareResponseSuccess::NotRequired(e) => {
PrepareDetailedResponseSuccess::NotRequired(e)
}
}))
}
PrepareResponse::Error(e) => Ok(PrepareDetailedResponse::Error(e)),
}
}

// TODO don't use "prepare" error type here. Maybe rename to generic request error?
pub async fn status(
&self,
orchestration_id: String,
) -> Result<StatusResponse, RouteError> {
) -> Result<StatusResponse, PrepareError> {
let response = self
.client
.get(self.base_url.join(STATUS_ENDPOINT_PATH).unwrap())
Expand All @@ -266,17 +301,17 @@ impl Client {
.timeout(Duration::from_secs(5))
.send()
.await
.map_err(RouteError::Request)?
.map_err(PrepareError::Request)?
.error_for_status()
.map_err(RouteError::Request)?;
.map_err(PrepareError::Request)?;
let status = response.status();
if status.is_success() {
let text =
response.text().await.map_err(RouteError::DecodingText)?;
response.text().await.map_err(PrepareError::DecodingText)?;
serde_json::from_str(&text)
.map_err(|e| RouteError::DecodingJson(e, text))
.map_err(|e| PrepareError::DecodingJson(e, text))
} else {
Err(RouteError::RequestFailed(response.text().await))
Err(PrepareError::RequestFailed(response.text().await))
}
}

Expand Down Expand Up @@ -329,7 +364,7 @@ impl Client {
}
},
Err(e) => {
(WaitForSuccessError::RouteError(e), Duration::from_secs(1))
(WaitForSuccessError::Prepare(e), Duration::from_secs(1))
// TODO exponential back-off: 0ms, 500ms, 1s
}
};
Expand All @@ -340,6 +375,14 @@ impl Client {
}
}

// pub async fn send() {
// // TODO input signed txns
// // TODO send route first, etc. (possibly in parallel)
// // TODO wait_for_success
// // TODO send initial transaction
// // TODO await initial transaction success
// }

pub async fn get_provider(&self, chain_id: String) -> ReqwestProvider {
let providers = self.providers.read().await;
if let Some(provider) = providers.get(&chain_id) {
Expand Down
Loading

0 comments on commit bd780a8

Please sign in to comment.