Skip to content

feat: algokit_utils (Rust impl only) #166

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

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open

feat: algokit_utils (Rust impl only) #166

wants to merge 45 commits into from

Conversation

joe-p
Copy link
Collaborator

@joe-p joe-p commented Jun 20, 2025

Follows up on the spike in #119 but removes the FFI utils crate and packages for now. When working on them I realized that they will take a decent amount of time to implement due do all the new types/traits, so figured it makes more sense to review the rust impl on its own and then later PR the FFI crate and packages

joe-p added 30 commits June 3, 2025 11:29
Had to work around this bug in maturin:
PyO3/maturin#2459

This fix is relatively simple but requires you to modify the packages
post-installation. A fix (more like feature) can be added to maturin and
I think it should be fairly straightforward
There is something weird going on with the env package though. For now
you need to bun rm env, run the build script, and then bun i env before
running the tests
Mutex works in WASM and only adds a few nanos of overhead, so just
easier to use Mutex for both uniffi and wasm
It works, but there seems to be some closure that isn't getting cleaned
up properly in the async call
Bun prefers main over module for some reason at runtime. See
oven-sh/bun#13430 (comment)
@joe-p joe-p marked this pull request as ready for review June 20, 2025 19:33
@Copilot Copilot AI review requested due to automatic review settings June 20, 2025 19:33
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR delivers the standalone Rust implementation of algokit_utils and related FFI-supporting crates, removes the interim FFI utils packaging, and updates workspace and build configurations accordingly.

  • Introduces ffi_mutex crate for unified mutex API across Tokio and WASM.
  • Adds algokit_utils crate and updates workspace member listings.
  • Adjusts build_pkgs script and FFI crate configurations.

Reviewed Changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tools/build_pkgs/src/python.rs Removes unused platform-specific ext logic.
crates/ffi_mutex/src/lib.rs Adds FfiMutex wrapper with Tokio and WASM variants.
crates/ffi_mutex/Cargo.toml Defines optional features and dependencies.
crates/algokit_utils/Cargo.toml Bootstraps the new algokit_utils crate.
crates/algokit_transact_ffi/Cargo.toml Updates crate-type entries to include lib.
crates/algokit_http_client_trait/src/lib.rs Implements HTTP client trait for native and WASM.
crates/algokit_http_client_trait/Cargo.toml Sets up features and dependencies for HTTP trait.
crates/algod_api/src/lib.rs Uses the HTTP trait to implement transaction_params.
crates/algod_api/Cargo.toml Configures default HTTP client feature.
Cargo.toml Updates workspace member list.
Comments suppressed due to low confidence (1)

crates/ffi_mutex/src/lib.rs:15

  • Add unit tests for FfiMutex to cover both ffi_uniffi and ffi_wasm feature branches, ensuring both locking methods behave as expected.
pub struct FfiMutex<T>(InnerMutex<T>);

/// WASM doesn't need to be thread-safe, but we still need the interior mutability so we can have the same API on our FfiMutex in both
/// cases (otherwise we have a bunch of different code paths in our implementations). We probably could use some unsafe code
/// (i.e `UnsafeCell`) in place of the `RefCell`, but to make our lives easier and avoid accidental UB we use `RefCell` instead. The
/// perofmrance difference is likely negligible in practice.
Copy link
Preview

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

There's a typo in this doc comment: 'perofmrance' should be 'performance'.

Suggested change
/// perofmrance difference is likely negligible in practice.
/// performance difference is likely negligible in practice.

Copilot uses AI. Check for mistakes.

#[async_trait(?Send)]
impl HttpClient for WasmHttpClient {
async fn get(&self, path: String) -> Result<Vec<u8>, HttpError> {
let result = self.get(&path).await.unwrap();
Copy link
Preview

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

Calling unwrap on the result of an external JS API can cause panics; consider propagating the JsValue error with map_err instead of unwrap to avoid runtime panics.

Suggested change
let result = self.get(&path).await.unwrap();
let result = self.get(&path)
.await
.map_err(|e| HttpError::HttpError(format!("JavaScript error: {:?}", e)))?;

Copilot uses AI. Check for mistakes.

}

#[cfg(feature = "ffi_wasm")]
pub async fn lock(&self) -> std::cell::RefMut<'_, T> {
Copy link
Preview

Copilot AI Jun 20, 2025

Choose a reason for hiding this comment

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

[nitpick] The lock method in the WASM branch is marked async but runs synchronously; consider removing async to simplify the API and avoid unnecessary Future overhead.

Suggested change
pub async fn lock(&self) -> std::cell::RefMut<'_, T> {
pub fn lock(&self) -> std::cell::RefMut<'_, T> {

Copilot uses AI. Check for mistakes.

pub min_fee: u64,
}

impl AlgodClient {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Probably worth putting some doc comments around this impl.

@@ -0,0 +1,19 @@
[package]
name = "algokit_http_client_trait"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Potentially we could call this algokit_http_client given that it optionally has a default implementation.

use async_trait::async_trait;

#[derive(Debug, Default, Clone)]
pub struct CommonParams {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We call these common fields TransactionHeader in algokit_transact.
I'm wondering if we align terminology?

if idx < txns.len() {
SignedTransaction {
transaction: txns[idx].clone(),
signature: Some([0; 64]),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Question: Is it better to this or use None? If we always expect a signature, then potentially this field shouldn't be optional?

SignedTransaction {
transaction: txns[idx].clone(),
signature: Some([0; 64]),
auth_address: None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

What the plan for supporting rekeyed accounts?

self.signer_getter.get_signer(address).await
}

// TODO: Use Fn defined in ComposerConfig
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this comment about adding support for caching suggested params?

.transactions
.iter()
.map(|composer_txn| {
let alread_formed_txn = matches!(composer_txn, ComposerTxn::Transaction(_));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let alread_formed_txn = matches!(composer_txn, ComposerTxn::Transaction(_));
let already_formed_txn = matches!(composer_txn, ComposerTxn::Transaction(_));

/// cases (otherwise we have a bunch of different code paths in our implementations). We probably could use some unsafe code
/// (i.e `UnsafeCell`) in place of the `RefCell`, but to make our lives easier and avoid accidental UB we use `RefCell` instead. The
/// perofmrance difference is likely negligible in practice.
pub struct FfiMutex<T>(InnerMutex<T>);
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we update Cargo.toml make the defaukt feature either uniffi or wasm? Without the default feature, rust analyer isn't happy because it doesn't know what is InnerMutex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants