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: execute function #125

Merged
merged 56 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d692e59
feat: execute function
Jan 29, 2025
ca21bff
chore: fix Swift build error
Jan 29, 2025
ba22ed6
chore: fmt
Jan 29, 2025
5dab721
chore: assert initial txn status
Jan 30, 2025
c2bf08d
chore: analytics details
Jan 31, 2025
47f0f63
chore: add eventId
Jan 31, 2025
a416127
chore: more analytics work
Feb 3, 2025
ae1533f
chore: fix status error
Feb 3, 2025
0c2b58a
chore: reduce bundle size
Feb 3, 2025
cc794b5
chore: refactor error handling
Feb 3, 2025
f438ee6
chore: remove unwrap()
Feb 3, 2025
989ed97
chore: refactor get_l1_data_fee error
Feb 3, 2025
7a63542
chore: serde::systemtime_millis
Feb 3, 2025
d84dd0f
chore: add orchestration_id to analytics
Feb 3, 2025
62d17c7
chore: fix Swift build
Feb 4, 2025
3fca968
chore: pulse metadata
Feb 4, 2025
166f347
chore: update Flutter bindings
github-actions[bot] Feb 4, 2025
4cb768c
chore: fix Kotlin build
Feb 4, 2025
76f359f
Merge branch 'feat/execute-function' of github.com:reown-com/yttrium …
Feb 4, 2025
32e6e51
chore: lax tests
Feb 4, 2025
90431a8
chore: fix test_blockchain_api tests not doing basic builds in CI
Feb 4, 2025
2c70fe7
chore: execute() test and fix Pulse API
Feb 4, 2025
906e41b
chore: fix yttrium tests not calling Pulse API
Feb 5, 2025
1fe9b43
chore: faster poll intervals
Feb 5, 2025
71c6519
chore: logs
Feb 5, 2025
3cb2ede
update actions
jakubuid Feb 5, 2025
768f6b6
update kotlin release
jakubuid Feb 5, 2025
8d4fc36
update kotlin release
jakubuid Feb 5, 2025
78061ed
add debugging
jakubuid Feb 5, 2025
30f4b5b
update kotlin release
jakubuid Feb 5, 2025
518e4b7
chore: support API version 23 by not using Duration
Feb 5, 2025
3a0e894
Merge branch 'feat/execute-function' of github.com:reown-com/yttrium …
Feb 5, 2025
f4be3f9
change ndk version
jakubuid Feb 5, 2025
1ab46ef
try without stripping
jakubuid Feb 5, 2025
3c8403a
rever stripping removal
jakubuid Feb 5, 2025
9ce457f
chore: expose prepare_detailed()
Feb 5, 2025
9b481d7
Merge branch 'feat/execute-function' of github.com:reown-com/yttrium …
Feb 5, 2025
d52d33a
chore: use prod endpoint
Feb 6, 2025
c241847
upload artufacts separately
jakubuid Feb 6, 2025
826749f
use seperate dirs
jakubuid Feb 6, 2025
8cd6ab4
build without the profile
jakubuid Feb 6, 2025
6af0d26
update release
jakubuid Feb 6, 2025
5b7e2ae
enable checkum
jakubuid Feb 7, 2025
654cc5d
update
jakubuid Feb 7, 2025
4f52123
chaneg profile otlin
jakubuid Feb 7, 2025
374765a
rustls-tls
jakubuid Feb 7, 2025
b629805
rustls-tls
jakubuid Feb 7, 2025
ce38541
chore: switch to default-tls
Feb 7, 2025
27cb578
chore: add Display logging
Feb 7, 2025
991542c
chore: bump UniFFI
Feb 7, 2025
5459b30
chore: keep checksums
Feb 7, 2025
afbc9da
revert comment
jakubuid Feb 7, 2025
faf1e12
chore: commit last-working Cargo.lock file for Kotlin build
Feb 9, 2025
e209bdc
update actions
jakubuid Feb 10, 2025
f8b7030
Merge branch 'chore/commit-last-working-kotlin-cargo-lock' of https:/…
jakubuid Feb 10, 2025
f8a5381
empty space
jakubuid Feb 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# for getrandom v0.3.*
# [target.wasm32-unknown-unknown]
# rustflags = '--cfg getrandom_backend="wasm_js"'
26 changes: 23 additions & 3 deletions crates/kotlin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
uniffi::setup_scaffolding!();

use alloy::primitives::{
ruint::aliases::U256, Address as FFIAddress, Bytes as FFIBytes, Uint,
U128 as FFIU128, U256 as FFIU256, U64 as FFIU64,
use alloy::{
hex,
primitives::{
ruint::aliases::U256, Address as FFIAddress, Bytes as FFIBytes,
PrimitiveSignature as FFIPrimitiveSignature, Uint, U128 as FFIU128,
U256 as FFIU256, U64 as FFIU64,
},
};
// Force import of this crate to ensure that the code is actually generated
#[allow(unused_imports)]
Expand Down Expand Up @@ -33,6 +37,7 @@ use {
relay_rpc::domain::ProjectId,
std::time::Duration,
yttrium::call::Call,
yttrium::chain_abstraction::client::ExecuteDetails,
yttrium::chain_abstraction::{
api::{
prepare::{PrepareResponse, PrepareResponseAvailable},
Expand All @@ -56,6 +61,12 @@ uniffi::custom_type!(FFIAddress, String, {
lower: |obj| obj.to_string(),
});

uniffi::custom_type!(FFIPrimitiveSignature, String, {
remote,
try_lift: |val| Ok(val.parse()?),
lower: |obj| format!("0x{}", hex::encode(obj.as_bytes())),
});

fn uint_to_hex<const BITS: usize, const LIMBS: usize>(
obj: Uint<BITS, LIMBS>,
) -> String {
Expand Down Expand Up @@ -189,6 +200,15 @@ impl ChainAbstractionClient {
.map_err(|e| FFIError::General(e.to_string()))
}

pub async fn execute(
&self,
ui_fields: UiFields,
route_txn_sigs: Vec<FFIPrimitiveSignature>,
initial_txn_sig: FFIPrimitiveSignature,
) -> ExecuteDetails {
self.client.execute(ui_fields, route_txn_sigs, initial_txn_sig).await
}

pub async fn estimate_fees(
&self,
chain_id: String,
Expand Down
13 changes: 10 additions & 3 deletions crates/yttrium/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ wasm = [
"dep:wasm-bindgen-futures",
"alloy/wasm-bindgen",
"dep:tsify-next",
# "getrandom/wasm_js", # for getrandom v0.3.*
"getrandom/js",
]

# Features contributing to bundle size, that you might not need
all_clients = ["account_client", "erc6492_client", "chain_abstraction_client", "transaction_sponsorship_client"]
all_clients = [
"account_client",
"erc6492_client",
"chain_abstraction_client",
"transaction_sponsorship_client",
]
account_client = []
erc6492_client = []
chain_abstraction_client = []
Expand Down Expand Up @@ -57,14 +64,14 @@ erc6492.workspace = true
relay_rpc.workspace = true

# foundry-block-explorers = "0.2.3"
getrandom = { version = "0.2", features = ["js"], default-features = false }
getrandom = { version = "0.2", default-features = false }

# Error/Result
eyre.workspace = true
thiserror.workspace = true

# Async
tokio.workspace = true
tokio = { workspace = true, features = ["rt"] }
futures.workspace = true

# Serialization
Expand Down
16 changes: 15 additions & 1 deletion crates/yttrium/src/chain_abstraction/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
use wasm_bindgen::prelude::*;
use {
alloy::{
consensus::{SignableTransaction, TxEip1559},
network::TransactionBuilder,
primitives::{Address, Bytes, U128, U256, U64},
primitives::{Address, Bytes, B256, U128, U256, U64},
rpc::types::TransactionRequest,
},
alloy_provider::utils::Eip1559Estimation,
Expand Down Expand Up @@ -92,4 +93,17 @@ impl FeeEstimatedTransaction {
.with_max_fee_per_gas(self.max_fee_per_gas.to())
.with_max_priority_fee_per_gas(self.max_priority_fee_per_gas.to())
}

pub fn into_eip1559(self) -> TxEip1559 {
self.into_transaction_request()
.build_unsigned()
.unwrap()
.eip1559()
.unwrap()
.clone()
}

pub fn into_signing_hash(self) -> B256 {
self.into_eip1559().signature_hash()
}
}
170 changes: 158 additions & 12 deletions crates/yttrium/src/chain_abstraction/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,47 @@ use {
crate::{
call::Call,
chain_abstraction::{
error::UiFieldsError, l1_data_fee::get_l1_data_fee, ui_fields,
error::UiFieldsError, l1_data_fee::get_l1_data_fee, pulse::pulse,
ui_fields,
},
erc20::ERC20,
provider_pool::ProviderPool,
},
alloy::{
consensus::{SignableTransaction, TxEnvelope},
network::TransactionBuilder,
primitives::{Address, U256, U64},
rpc::types::TransactionRequest,
primitives::{Address, PrimitiveSignature, B256, U256, U64},
rpc::types::{TransactionReceipt, TransactionRequest},
},
alloy_provider::{utils::Eip1559Estimation, Provider},
relay_rpc::domain::ProjectId,
reqwest::Client as ReqwestClient,
serde::{Deserialize, Serialize},
std::{
collections::{HashMap, HashSet},
time::{Duration, Instant},
time::{Duration, Instant, SystemTime, UNIX_EPOCH},
},
thiserror::Error,
};

#[derive(Clone)]
pub struct Client {
provider_pool: ProviderPool,
http_client: ReqwestClient,
project_id: ProjectId,
}

impl Client {
pub fn new(project_id: ProjectId) -> Self {
Self { provider_pool: ProviderPool::new(project_id) }
let client = ReqwestClient::new();
Self {
provider_pool: ProviderPool::new(
project_id.clone(),
client.clone(),
),
http_client: client,
project_id,
}
}

pub async fn prepare(
Expand Down Expand Up @@ -396,13 +411,110 @@ 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 execute(
&self,
ui_fields: UiFields,
chris13524 marked this conversation as resolved.
Show resolved Hide resolved
route_txn_sigs: Vec<PrimitiveSignature>,
chris13524 marked this conversation as resolved.
Show resolved Hide resolved
initial_txn_sig: PrimitiveSignature,
) -> ExecuteDetails {
assert_eq!(
ui_fields.route.len(),
route_txn_sigs.len(),
"route_txn_sigs length must match route length"
);

let start = Instant::now();
let start_time = SystemTime::now();

let route_start = start;
let mut route_latencies = Vec::with_capacity(ui_fields.route.len());
for (txn, sig) in
ui_fields.route.into_iter().zip(route_txn_sigs.into_iter())
{
let route_i_start = Instant::now();
let provider = self
.provider_pool
.get_provider(&txn.transaction.chain_id)
.await;
let signed = txn.transaction.into_eip1559().into_signed(sig);
assert!(provider
.send_tx_envelope(TxEnvelope::Eip1559(signed))
.await
.unwrap()
.with_timeout(Some(Duration::from_secs(15)))
.get_receipt()
.await
.unwrap()
.status());
route_latencies.push(route_i_start.elapsed());
}
let route_latency = route_start.elapsed();

let status_start = Instant::now();
let _success = self
.wait_for_success(
ui_fields.route_response.orchestration_id,
Duration::from_millis(
ui_fields.route_response.metadata.check_in,
),
)
.await
.unwrap();
let status_latency = status_start.elapsed();

let initial_txn_start = Instant::now();
let provider = self
.provider_pool
.get_provider(&ui_fields.initial.transaction.chain_id)
.await;
let signed = ui_fields
.initial
.transaction
.into_eip1559()
.into_signed(initial_txn_sig);
let initial_txn_receipt = provider
.send_tx_envelope(TxEnvelope::Eip1559(signed))
.await
.unwrap()
.with_timeout(Some(Duration::from_secs(15)))
.get_receipt()
chris13524 marked this conversation as resolved.
Show resolved Hide resolved
.await
.unwrap();
let initial_latency = initial_txn_start.elapsed();
assert!(initial_txn_receipt.status());

let details = ExecuteDetails {
initial_txn_hash: initial_txn_receipt.transaction_hash,
initial_txn_receipt,
};

let latency = start.elapsed();

pulse(
self.http_client.clone(),
ExecuteAnalytics {
error: None,
start: start_time
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis(),
route_latency,
route_latencies,
route_txn_hashes: vec![],
status_latency,
initial_latency,
latency,
initial_txn_hash: details.initial_txn_hash,
end: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis(),
},
self.project_id.clone(),
);

details
}

pub async fn erc20_token_balance(
&self,
Expand All @@ -416,3 +528,37 @@ impl Client {
Ok(balance.balance)
}
}

#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecuteDetails {
pub initial_txn_receipt: TransactionReceipt,
pub initial_txn_hash: B256,
}

#[derive(Debug, Error)]
pub enum ExecuteError {
#[error("Route error: {0}")]
RouteError(String),
#[error("Status error: {0}")]
StatusError(String),
#[error("Initial error: {0}")]
InitialError(String),
}

#[cfg_attr(feature = "uniffi", derive(uniffi_macros::Record))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecuteAnalytics {
pub error: Option<String>,
pub start: u128,
pub route_latency: Duration,
pub route_latencies: Vec<Duration>,
pub route_txn_hashes: Vec<B256>,
pub status_latency: Duration,
pub initial_latency: Duration,
pub initial_txn_hash: B256,
pub latency: Duration,
pub end: u128,
}

// TODO test non-happy paths: txn failures
2 changes: 2 additions & 0 deletions crates/yttrium/src/chain_abstraction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub mod currency;
pub mod error;
pub mod l1_data_fee;
pub mod local_fee_acc;
pub mod pulse;
pub mod spawn;
pub mod ui_fields;

#[cfg(test)]
Expand Down
Loading
Loading