Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fungible metadata amount utils + get_route_ui_fields() FFI #78

Merged
merged 11 commits into from
Dec 9, 2024
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ jobs:
uses: mozilla-actions/[email protected]
- run: rustup update stable && rustup default stable
- run: make build-xcframework
- name: Select Xcode 15.4
run: sudo xcode-select -s /Applications/Xcode_15.4.app
- name: Build ${{ matrix.config }}
run: make CONFIG=${{ matrix.config }} build-swift-apple-platforms
# So confused on what to run for Swift CI
# - name: Select Xcode 15.4
# run: sudo xcode-select -s /Applications/Xcode_15.4.app
# - name: Build ${{ matrix.config }}
# run: make CONFIG=${{ matrix.config }} build-swift-apple-platforms
chris13524 marked this conversation as resolved.
Show resolved Hide resolved
# - name: Install Docker
# run: |
# HOMEBREW_NO_AUTO_UPDATE=1 brew install --cask docker
Expand Down
112 changes: 95 additions & 17 deletions crates/kotlin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ uniffi::setup_scaffolding!();
use {
alloy::{
network::Ethereum,
primitives::{Bytes as FFIBytes, U256 as FFIU256, U64 as FFIU64},
primitives::{
Bytes as FFIBytes, U128 as FFIU128, U256 as FFIU256, U64 as FFIU64,
},
providers::{Provider, ReqwestProvider},
},
relay_rpc::domain::ProjectId,
std::time::Duration,
yttrium::{
account_client::{AccountClient as YAccountClient, SignerType},
chain_abstraction::{
self,
amount::Amount,
api::{
route::RouteResponse,
route::{RouteResponse, RouteResponseAvailable},
status::{StatusResponse, StatusResponseCompleted},
Transaction as CATransaction,
},
client::Client,
client::{Client, TransactionFee},
currency::Currency,
},
config::Config,
private_key_service::PrivateKeyService,
Expand Down Expand Up @@ -53,12 +58,17 @@ uniffi::custom_type!(FFIAddress, String, {
lower: |obj| obj.to_string(),
});

uniffi::custom_type!(FFIU256, String, {
uniffi::custom_type!(FFIU64, String, {
try_lift: |val| Ok(val.parse()?),
lower: |obj| obj.to_string(),
});

uniffi::custom_type!(FFIU64, String, {
uniffi::custom_type!(FFIU128, String, {
try_lift: |val| Ok(val.parse()?),
lower: |obj| obj.to_string(),
});

uniffi::custom_type!(FFIU256, String, {
try_lift: |val| Ok(val.parse()?),
lower: |obj| obj.to_string(),
});
Expand All @@ -68,6 +78,72 @@ uniffi::custom_type!(FFIBytes, String, {
lower: |obj| obj.to_string(),
});

#[derive(Debug, uniffi::Record)]
pub struct RouteUiFields {
pub route: Vec<TxnDetails>,
pub bridge: Vec<TransactionFee>,
jakubuid marked this conversation as resolved.
Show resolved Hide resolved
pub initial: TxnDetails,
pub local_total: Amount,
}

impl From<yttrium::chain_abstraction::client::RouteUiFields> for RouteUiFields {
fn from(source: yttrium::chain_abstraction::client::RouteUiFields) -> Self {
Self {
route: source.route.into_iter().map(Into::into).collect(),
bridge: source.bridge,
initial: source.initial.into(),
local_total: source.local_total,
}
}
}

#[derive(Debug, uniffi::Record)]
pub struct TxnDetails {
pub transaction: CATransaction,
pub estimate: Eip1559Estimation,
pub fee: TransactionFee,
}

impl From<yttrium::chain_abstraction::client::TxnDetails> for TxnDetails {
fn from(source: yttrium::chain_abstraction::client::TxnDetails) -> Self {
Self {
transaction: source.transaction,
estimate: source.estimate.into(),
fee: source.fee,
}
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, uniffi::Record)]
pub struct Eip1559Estimation {
/// The base fee per gas.
pub max_fee_per_gas: FFIU128,
/// The max priority fee per gas.
pub max_priority_fee_per_gas: FFIU128,
}

impl From<alloy::providers::utils::Eip1559Estimation> for Eip1559Estimation {
fn from(source: alloy::providers::utils::Eip1559Estimation) -> Self {
Self {
max_fee_per_gas: FFIU128::from(source.max_fee_per_gas),
max_priority_fee_per_gas: FFIU128::from(
source.max_priority_fee_per_gas,
),
}
}
}

// uniffi::custom_type!(Eip1559Estimation, FfiEip1559Estimation, {
// try_lift: |val| Ok(Eip1559Estimation {
// max_fee_per_gas: val.max_fee_per_gas.to(),
// max_priority_fee_per_gas: val.max_priority_fee_per_gas.to(),
// }),
// lower: |obj| FfiEip1559Estimation {
// max_fee_per_gas: U128::from(obj.max_fee_per_gas),
// max_priority_fee_per_gas: U128::from(obj.max_priority_fee_per_gas),
// },
// });
chris13524 marked this conversation as resolved.
Show resolved Hide resolved

#[derive(uniffi::Record)]
pub struct InitTransaction {
pub from: FFIAddress,
Expand Down Expand Up @@ -113,12 +189,6 @@ pub struct ChainAbstractionClient {
client: Client,
}

#[derive(uniffi::Record)]
pub struct Eip1559Estimation {
pub max_fee_per_gas: String,
pub max_priority_fee_per_gas: String,
}

#[uniffi::export(async_runtime = "tokio")]
impl ChainAbstractionClient {
#[uniffi::constructor]
Expand All @@ -138,6 +208,19 @@ impl ChainAbstractionClient {
.map_err(|e| FFIError::General(e.to_string()))
}

pub async fn get_route_ui_fields(
&self,
route_response: RouteResponseAvailable,
initial_transaction: chain_abstraction::api::Transaction,
currency: Currency,
) -> Result<RouteUiFields, FFIError> {
self.client
.get_route_ui_fields(route_response, initial_transaction, currency)
.await
.map(Into::into)
.map_err(|e| FFIError::General(e.to_string()))
}

pub async fn status(
&self,
orchestration_id: String,
Expand Down Expand Up @@ -178,13 +261,8 @@ impl ChainAbstractionClient {
provider
.estimate_eip1559_fees(None)
.await
.map(Into::into)
.map_err(|e| FFIError::General(e.to_string()))
.map(|fees| Eip1559Estimation {
max_fee_per_gas: fees.max_fee_per_gas.to_string(),
max_priority_fee_per_gas: fees
.max_priority_fee_per_gas
.to_string(),
})
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/yttrium/src/chain_abstraction/amount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 2 additions & 17 deletions crates/yttrium/src/chain_abstraction/api/fungible_price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Unit, D::Error>
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<S>(unit: &Unit, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(unit.get())
}
29 changes: 28 additions & 1 deletion crates/yttrium/src/chain_abstraction/api/route.rs
Original file line number Diff line number Diff line change
@@ -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},
};
Expand Down Expand Up @@ -33,8 +34,34 @@ pub struct FundingMetadata {
pub chain_id: String,
pub token_contract: Address,
pub symbol: String,

// The amount that was sourced (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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can use U8 here instead?

Copy link
Member Author

@chris13524 chris13524 Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I think we should use Unit since it's alloy's type to represent these decimals. It has built-in assertions that the value is less than 77, so we know it's a valid unit. Rust code can also have fewer error conditions when this type is already being used by calling code.

}

// 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)]
Expand Down
28 changes: 20 additions & 8 deletions crates/yttrium/src/chain_abstraction/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Client {
initial_transaction: Transaction,
currency: Currency,
// TODO use this to e.g. modify priority fee
_speed: String,
// _speed: String,
) -> Result<RouteUiFields, RouteUiFieldsError> {
let chains = route_response
.transactions
Expand Down Expand Up @@ -307,7 +307,11 @@ impl Client {
})
.unwrap(),
);
route.push((item.0, item.1, fee));
route.push(TxnDetails {
transaction: item.0,
estimate: item.1,
fee,
});
}

let initial_fee = compute_amounts(
Expand All @@ -325,11 +329,11 @@ impl Client {
})
.unwrap(),
);
let initial = (
estimated_initial_transaction.0,
estimated_initial_transaction.1,
initial_fee,
);
let initial = TxnDetails {
transaction: estimated_initial_transaction.0,
estimate: estimated_initial_transaction.1,
fee: initial_fee,
};

let mut bridge =
Vec::with_capacity(route_response.metadata.funding_from.len());
Expand Down Expand Up @@ -490,16 +494,24 @@ impl Client {
}

#[derive(Debug)]
// #[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
jakubuid marked this conversation as resolved.
Show resolved Hide resolved
pub struct RouteUiFields {
pub route: Vec<TxnDetails>,
pub bridge: Vec<TransactionFee>,
pub initial: TxnDetails,
pub local_total: Amount,
}

pub type TxnDetails = (Transaction, Eip1559Estimation, TransactionFee);
#[derive(Debug)]
// #[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
chris13524 marked this conversation as resolved.
Show resolved Hide resolved
pub struct TxnDetails {
pub transaction: Transaction,
pub estimate: Eip1559Estimation,
pub fee: TransactionFee,
}

#[derive(Debug)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
pub struct TransactionFee {
pub fee: Amount,
pub local_fee: Amount,
Expand Down
1 change: 1 addition & 0 deletions crates/yttrium/src/chain_abstraction/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use {
// TODO get Blockchain API to use these types?

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Enum))]
#[serde(rename_all = "lowercase")]
pub enum Currency {
Usd,
Expand Down
Loading
Loading