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 38 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"'
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ jobs:
- run: while ! curl localhost:8545/health; do sleep 1; done
- run: while ! curl localhost:4337/health; do sleep 1; done
- run: while ! curl localhost:3000/ping; do sleep 1; done
- run: cargo build --workspace --features=full --all-targets
- run: cargo build --workspace --all-features --all-targets
- run: cargo build -p yttrium --features=wasm --lib --target wasm32-unknown-unknown
- run: cargo test --features=full --lib --bins
- run: RUST_BACKTRACE=1 RUST_LOG=yttrium=trace cargo test --features=full --lib --bins
env:
REOWN_PROJECT_ID: ${{ vars.REOWN_PROJECT_ID }}
- run: cargo clippy --workspace --features=full --all-targets -- -D warnings
- run: cargo clippy --workspace --all-features --all-targets -- -D warnings
- run: cargo clippy -p yttrium --all-targets -- -D warnings # `-p yttrium` to avoid feature unification; kotlin-ffi enables uniffi feature and we want to test without that
- run: cargo +nightly fmt --all -- --check
- run: cargo install cargo-udeps --locked
Expand Down
87 changes: 73 additions & 14 deletions .github/workflows/release-kotlin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ permissions:
jobs:
build-kotlin-artifacts:
runs-on: ubuntu-latest

strategy:
matrix:
target: [aarch64-linux-android, armv7-linux-androideabi]
Expand Down Expand Up @@ -53,24 +52,27 @@ jobs:

- name: Install cargo-ndk
run: |
# --locked to bypass this pipeline error: https://github.com/reown-com/yttrium/actions/runs/12717232881/job/35453267940?pr=110
# --locked to bypass pipeline error in some contexts
cargo install cargo-ndk --locked

- name: Build Rust library
run: |
cargo ndk -t ${{ matrix.target }} build --profile=uniffi-release-kotlin --features=uniffi/cli

- name: Generate Kotlin bindings (once)
- name: Generate Kotlin bindings (once, on aarch64 only)
if: ${{ matrix.target == 'aarch64-linux-android' }}
run: |
cargo run --features=uniffi/cli --bin uniffi-bindgen generate --library target/${{ matrix.target }}/uniffi-release-kotlin/libuniffi_yttrium.so --language kotlin --out-dir yttrium/kotlin-bindings
cargo run --features=uniffi/cli --bin uniffi-bindgen generate \
--library target/${{ matrix.target }}/uniffi-release-kotlin/libuniffi_yttrium.so \
--language kotlin \
--out-dir yttrium/kotlin-bindings

- name: Strip binaries
run: |
NDK_PATH=$ANDROID_HOME/ndk/27.2.12479018
$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip target/${{ matrix.target }}/uniffi-release-kotlin/libuniffi_yttrium.so

- name: Prepare artifacts
- name: Prepare local artifacts folder
run: |
# Map Rust targets to Android ABI names
declare -A abi_map
Expand All @@ -83,25 +85,82 @@ jobs:
exit 1
fi

mkdir -p yttrium/libs/$abi_name
cp target/${{ matrix.target }}/uniffi-release-kotlin/libuniffi_yttrium.so yttrium/libs/$abi_name/
# Create a local folder with everything for this target
mkdir -p build-artifacts/yttrium/libs/$abi_name
cp target/${{ matrix.target }}/uniffi-release-kotlin/libuniffi_yttrium.so build-artifacts/yttrium/libs/$abi_name/

# Also copy Kotlin bindings if they exist (only for aarch64)
if [ -d "yttrium/kotlin-bindings" ]; then
cp -r yttrium/kotlin-bindings build-artifacts/yttrium/
fi

- name: Upload artifact (unique name per target)
uses: actions/upload-artifact@v4
with:
name: artifacts-${{ matrix.target }}
path: build-artifacts/

combine-artifacts:
needs: build-kotlin-artifacts
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Download aarch64 artifact
uses: actions/download-artifact@v4
with:
name: artifacts-aarch64-linux-android
path: combined/aarch64

- name: Debug listing of downloaded artifact aarch64
run: |
echo "Contents of combined/aarch64:"
ls -R combined/aarch64

- name: Upload artifacts
uses: actions/upload-artifact@v3
- name: Download armv7 artifact
uses: actions/download-artifact@v4
with:
name: artifacts-armv7-linux-androideabi
path: combined/armv7

- name: Debug listing of downloaded artifact armv7
run: |
echo "Contents of combined/armv7:"
ls -R combined/armv7

- name: Merge artifacts
run: |
# We'll create one combined folder that contains all ABIs.
# For example, copy the libs into the same 'yttrium/libs/...' structure
mkdir -p merged/yttrium/libs

# Copy aarch64's libs
cp -r combined/aarch64/yttrium/libs/arm64-v8a merged/yttrium/libs/

# Copy armv7's libs
cp -r combined/armv7/yttrium/libs/armeabi-v7a merged/yttrium/libs/

# Copy Kotlin bindings if it exists in the aarch64 artifact
if [ -d combined/aarch64/yttrium/kotlin-bindings ]; then
cp -r combined/aarch64/yttrium/kotlin-bindings merged/yttrium/
fi

- name: Upload single combined artifact
uses: actions/upload-artifact@v4
with:
name: artifacts
path: yttrium/
path: merged/yttrium/

create-github-release:
needs: build-kotlin-artifacts
needs: combine-artifacts
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Download artifacts
uses: actions/download-artifact@v3
- name: Download single final artifact
uses: actions/download-artifact@v4
with:
name: artifacts
path: yttrium/
Expand All @@ -117,7 +176,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ env.VERSION }}
release_name: Yttrium ${{ env.VERSION }}
release_name: "Yttrium ${{ env.VERSION }}"
draft: false
prerelease: false

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ futures = { version = "0.3.31", default-features = false }

# Networking
reqwest = { version = "0.12.5", features = ["json"], default-features = false }
url = { version = "2.5.4", default-features = false }

# Serialization
serde = { version = "1.0", features = ["derive"], default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion build-wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -eo pipefail
WASM_FLAGS="${WASM_FLAGS:-}"

cd crates/yttrium
wasm-pack build --target web --features=wasm $WASM_FLAGS
RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" rustup run nightly wasm-pack build --target web --features=wasm $WASM_FLAGS -Z build-std=std,panic_abort -Z build-std-features=optimize_for_size,panic_immediate_abort
mkdir -p ../../benchmark/build-wasm/web/
stat -f%z pkg/yttrium_bg.wasm > ../../benchmark/build-wasm/web/yttrium_bg.wasm.size

Expand Down
62 changes: 54 additions & 8 deletions crates/kotlin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
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,
};
// Force import of this crate to ensure that the code is actually generated
#[allow(unused_imports)]
#[allow(clippy::single_component_path_imports)]
Expand All @@ -22,6 +18,19 @@ use {
smart_accounts::safe::{SignOutputEnum, SignStep3Params},
},
};
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,
},
},
yttrium::chain_abstraction::{
error::PrepareDetailedResponse, pulse::PulseMetadata,
},
};
#[cfg(feature = "chain_abstraction_client")]
use {
alloy::{
Expand All @@ -33,6 +42,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 +66,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 @@ -135,8 +151,9 @@ pub struct ChainAbstractionClient {
#[uniffi::export(async_runtime = "tokio")]
impl ChainAbstractionClient {
#[uniffi::constructor]
pub fn new(project_id: String) -> Self {
let client = Client::new(ProjectId::from(project_id.clone()));
pub fn new(project_id: String, pulse_metadata: PulseMetadata) -> Self {
let client =
Client::new(ProjectId::from(project_id.clone()), pulse_metadata);
Self { project_id, client }
}

Expand All @@ -155,10 +172,25 @@ impl ChainAbstractionClient {
pub async fn get_ui_fields(
&self,
route_response: PrepareResponseAvailable,
currency: Currency,
local_currency: Currency,
) -> Result<UiFields, FFIError> {
self.client
.get_ui_fields(route_response, currency)
.get_ui_fields(route_response, local_currency)
.await
.map_err(|e| FFIError::General(e.to_string()))
}

pub async fn prepare_detailed(
&self,
chain_id: String,
from: FFIAddress,
call: Call,
local_currency: Currency,
// TODO use this to e.g. modify priority fee
// _speed: String,
) -> Result<PrepareDetailedResponse, FFIError> {
self.client
.prepare_detailed(chain_id, from, call, local_currency)
.await
.map_err(|e| FFIError::General(e.to_string()))
}
Expand Down Expand Up @@ -189,6 +221,20 @@ 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,
) -> Result<ExecuteDetails, FFIError> {
self.client
.execute(ui_fields, route_txn_sigs, initial_txn_sig)
.await
// TODO wanted to return ExecuteError directly here, but can't because Swift keeps the UniFFI lifer private to the yttrium crate and not available to kotlin-ffi crate
// This will be fixed when we merge these crates
.map_err(|e| FFIError::General(e.to_string()))
}

pub async fn estimate_fees(
&self,
chain_id: String,
Expand Down
29 changes: 25 additions & 4 deletions crates/yttrium/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ wasm = [
"dep:wasm-bindgen-futures",
"alloy/wasm-bindgen",
"dep:tsify-next",
"dep:derive_jserror",
# "getrandom/wasm_js", # for getrandom v0.3.*
"getrandom/js",
"uuid/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 +66,19 @@ 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 }

uuid = { version = "1.12.1", default-features = false, features = [
"v4",
"serde",
] }

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

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

# Serialization
Expand All @@ -77,15 +91,22 @@ dotenvy = { version = "0.15.7", default-features = false }
# Other
hex = { version = "0.4.3", features = ["std"], default-features = false }
async-trait = { version = "0.1.83", default-features = false }
tracing = { version = "0.1.40", default-features = false }
tracing = { version = "0.1.40", default-features = false, features = [
"release_max_level_warn",
] }
# fastlz-rs = { version = "0.0.3", features = ["std"], default-features = false }

# Networking
reqwest.workspace = true
url = { workspace = true, features = ["serde"] }

# Tiny dependency, could copy source. Pinning to exact version to avoid supply-chain attacks.
derive_jserror = { version = "=0.1.0", optional = true, default-features = false }

[dev-dependencies]
# mocking
wiremock = { version = "0.6.0", default-features = false }
test-log = { version = "0.2.17", default-features = false, features = ["trace"] }

# Networking
reqwest.workspace = true
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()
}
}
3 changes: 3 additions & 0 deletions crates/yttrium/src/chain_abstraction/api/prepare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ impl CallOrCalls {
pub struct Metadata {
pub funding_from: Vec<FundingMetadata>,
pub initial_transaction: InitialTransactionMetadata,
/// The number of milliseconds to delay before calling `/status` after getting successful transaction receipts from all sent transactions.
/// Not switching to Duration yet because Kotlin maps this to a native `duration` type but this requires API version 26 but we support 23.
/// https://reown-inc.slack.com/archives/C07HQ8RCGD8/p1738740204879269
pub check_in: u64,
}

Expand Down
Loading
Loading