From 8017600bbba003414712b059337d34ab91ba4d50 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 5 Dec 2024 09:40:29 -0500 Subject: [PATCH] feat: fungible metadata amount utils --- .../yttrium/src/chain_abstraction/amount.rs | 1 + .../chain_abstraction/api/fungible_price.rs | 19 +----- .../src/chain_abstraction/api/route.rs | 29 ++++++++- crates/yttrium/src/chain_abstraction/tests.rs | 65 +++++++++++++++++++ crates/yttrium/src/lib.rs | 1 + crates/yttrium/src/uniffi_compat.rs | 21 +++++- crates/yttrium/src/utils.rs | 17 +++++ 7 files changed, 134 insertions(+), 19 deletions(-) create mode 100644 crates/yttrium/src/utils.rs diff --git a/crates/yttrium/src/chain_abstraction/amount.rs b/crates/yttrium/src/chain_abstraction/amount.rs index 2195646d..9d39d96e 100644 --- a/crates/yttrium/src/chain_abstraction/amount.rs +++ b/crates/yttrium/src/chain_abstraction/amount.rs @@ -4,6 +4,7 @@ use alloy::primitives::{ }; #[derive(Debug)] +#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))] pub struct Amount { pub symbol: String, // USDC, USD pub amount: U256, // e.g. 40000, 4 diff --git a/crates/yttrium/src/chain_abstraction/api/fungible_price.rs b/crates/yttrium/src/chain_abstraction/api/fungible_price.rs index f9f2b0b1..4f977065 100644 --- a/crates/yttrium/src/chain_abstraction/api/fungible_price.rs +++ b/crates/yttrium/src/chain_abstraction/api/fungible_price.rs @@ -40,23 +40,8 @@ pub struct FungiblePriceItem { pub icon_url: String, pub price: f64, #[serde( - deserialize_with = "deserialize_unit", - serialize_with = "serialize_unit" + deserialize_with = "crate::utils::deserialize_unit", + serialize_with = "crate::utils::serialize_unit" )] pub decimals: Unit, } - -fn deserialize_unit<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - Unit::new(u8::deserialize(deserializer)?) - .ok_or(serde::de::Error::custom("Unit must be less than 77")) -} - -fn serialize_unit(unit: &Unit, serializer: S) -> Result -where - S: serde::Serializer, -{ - serializer.serialize_u8(unit.get()) -} diff --git a/crates/yttrium/src/chain_abstraction/api/route.rs b/crates/yttrium/src/chain_abstraction/api/route.rs index 0be4997c..985354d7 100644 --- a/crates/yttrium/src/chain_abstraction/api/route.rs +++ b/crates/yttrium/src/chain_abstraction/api/route.rs @@ -1,6 +1,7 @@ use { super::Transaction, - alloy::primitives::{Address, U256}, + crate::chain_abstraction::amount::Amount, + alloy::primitives::{utils::Unit, Address, U256}, relay_rpc::domain::ProjectId, serde::{Deserialize, Serialize}, }; @@ -33,8 +34,34 @@ pub struct FundingMetadata { pub chain_id: String, pub token_contract: Address, pub symbol: String, + + // The amount that was sources (includes the bridging fee) pub amount: U256, + + // The amount taken by the bridge as a fee pub bridging_fee: U256, + + #[serde( + deserialize_with = "crate::utils::deserialize_unit", + serialize_with = "crate::utils::serialize_unit" + )] + #[serde(default = "default_unit")] + pub decimals: Unit, +} + +// TODO remove default when Blockchain API is updated to provide this +fn default_unit() -> Unit { + Unit::new(6).unwrap() +} + +impl FundingMetadata { + pub fn to_amount(&self) -> Amount { + Amount::new(self.symbol.clone(), self.amount, self.decimals) + } + + pub fn to_bridging_fee_amount(&self) -> Amount { + Amount::new(self.symbol.clone(), self.bridging_fee, self.decimals) + } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/yttrium/src/chain_abstraction/tests.rs b/crates/yttrium/src/chain_abstraction/tests.rs index aa8f0f9f..d2496bd3 100644 --- a/crates/yttrium/src/chain_abstraction/tests.rs +++ b/crates/yttrium/src/chain_abstraction/tests.rs @@ -1374,6 +1374,71 @@ async fn happy_path_full_dependency_on_route_ui_fields() { result.transactions[0].gas = U64::from(60000 /* 55437 */); // until Blockchain API estimates this result.transactions[1].gas = U64::from(140000 /* 107394 */); // until Blockchain API estimates this + assert_eq!(result.metadata.funding_from.len(), 1); + assert_eq!(result.metadata.funding_from.first().unwrap().symbol, "USDC"); + assert_eq!( + result.metadata.funding_from.first().unwrap().decimals, + Unit::new(6).unwrap() + ); + assert_eq!( + result + .metadata + .funding_from + .first() + .unwrap() + .clone() + .to_amount() + .symbol, + "USDC" + ); + assert!(result + .metadata + .funding_from + .first() + .unwrap() + .to_amount() + .formatted + .ends_with(" USDC")); + println!( + "{}", + result.metadata.funding_from.first().unwrap().to_amount().formatted + ); + assert!(result + .metadata + .funding_from + .first() + .unwrap() + .to_amount() + .formatted + .starts_with("1.5")); + assert!(result + .metadata + .funding_from + .first() + .unwrap() + .to_bridging_fee_amount() + .formatted + .starts_with("0.")); + assert!( + result.metadata.funding_from.first().unwrap().amount <= required_amount + ); + assert!(result.metadata.funding_from.first().unwrap().amount > send_amount); + assert!( + result.metadata.funding_from.first().unwrap().bridging_fee > U256::ZERO + ); + assert!( + result.metadata.funding_from.first().unwrap().bridging_fee + < send_amount / U256::from(2) + ); + assert_eq!( + result.metadata.funding_from.first().unwrap().chain_id, + source.bridge_token(&sources).params.chain.eip155_chain_id() + ); + assert_eq!( + &result.metadata.funding_from.first().unwrap().token_contract, + source.bridge_token(&sources).token.address() + ); + let start = Instant::now(); let route_ui_fields = client .get_route_ui_fields( diff --git a/crates/yttrium/src/lib.rs b/crates/yttrium/src/lib.rs index 8c0b07c9..06404dc0 100644 --- a/crates/yttrium/src/lib.rs +++ b/crates/yttrium/src/lib.rs @@ -23,6 +23,7 @@ pub mod smart_accounts; pub mod test_helpers; pub mod transaction; pub mod user_operation; +pub mod utils; #[cfg(test)] pub mod examples; diff --git a/crates/yttrium/src/uniffi_compat.rs b/crates/yttrium/src/uniffi_compat.rs index aa238c49..a542a911 100644 --- a/crates/yttrium/src/uniffi_compat.rs +++ b/crates/yttrium/src/uniffi_compat.rs @@ -1,4 +1,6 @@ -use alloy::primitives::{Address, Bytes, B256, U256, U64}; +use crate::chain_abstraction::amount::Amount; +use crate::chain_abstraction::api::route::FundingMetadata; +use alloy::primitives::{utils::Unit, Address, Bytes, B256, U256, U64}; // TODO use https://mozilla.github.io/uniffi-rs/next/udl/remote_ext_types.html#remote-types when it's available @@ -26,3 +28,20 @@ uniffi::custom_type!(B256, String, { try_lift: |val| Ok(val.parse()?), lower: |obj| obj.to_string(), }); + +uniffi::custom_type!(Unit, u8, { + try_lift: |val| Ok(Unit::new(val).expect("Unit must be less than 77")), + lower: |obj| obj.get(), +}); + +#[cfg(feature = "uniffi")] +#[uniffi::export] +fn funding_metadata_to_amount(value: FundingMetadata) -> Amount { + value.to_amount() +} + +#[cfg(feature = "uniffi")] +#[uniffi::export] +fn funding_metadata_to_bridging_fee_amount(value: FundingMetadata) -> Amount { + value.to_bridging_fee_amount() +} diff --git a/crates/yttrium/src/utils.rs b/crates/yttrium/src/utils.rs new file mode 100644 index 00000000..53dab225 --- /dev/null +++ b/crates/yttrium/src/utils.rs @@ -0,0 +1,17 @@ +use alloy::primitives::utils::Unit; +use serde::Deserialize; + +pub fn deserialize_unit<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + Unit::new(u8::deserialize(deserializer)?) + .ok_or(serde::de::Error::custom("Unit must be less than 77")) +} + +pub fn serialize_unit(unit: &Unit, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_u8(unit.get()) +}