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: kotlin bindings #30

Merged
merged 26 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ Package.resolved
Utilities/InstalledSwiftPMConfiguration/config.json
.devcontainer
.cursorignore
/crates/kotlin-ffi/jniLibs
/crates/kotlin-ffi/bindings/com/reown/kotlin/ffi/yttrium
34 changes: 34 additions & 0 deletions crates/kotlin-ffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "kotlin-ffi"
version = "0.1.0"
edition = "2021"

[lib]
name = "uniffi_yttrium"
crate-type = ["cdylib"]

[build-dependencies]
uniffi_build = "0.28.1"

[dependencies]
yttrium = { path = "../yttrium" }
uniffi = { version = "0.28.1", features = ["tokio"] }
openssl = { version = "0.10", features = ["vendored"] }
openssl-sys = { version = "0.9.103", features = ["vendored"] }

# Errors
eyre.workspace = true

# Async
tokio.workspace = true

# Serialization
serde.workspace = true
serde_json.workspace = true

# Networking
reqwest.workspace = true

# Logging
log.workspace = true
thiserror.workspace = true
3 changes: 3 additions & 0 deletions crates/kotlin-ffi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
uniffi_build::generate_scaffolding("src/yttrium.udl").unwrap();
}
53 changes: 53 additions & 0 deletions crates/kotlin-ffi/src/account_client.udl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace yttrium {

};

[Error]
enum Error {
"Unknown"
};

dictionary Endpoint {
string api_key;
string base_url;
};

dictionary Endpoints {
Endpoint rpc;
Endpoint bundler;
Endpoint paymaster;
};

dictionary Config {
Endpoints endpoints;
};

dictionary AccountClientConfig {
string owner_address;
u64 chain_id;
Config config;
string signer_type;
boolean safe;
string private_key;
};

dictionary Transaction {
string to;
string value;
string data;
};

interface AccountClient {
constructor(AccountClientConfig config);

u64 chain_id();

[Throws=Error, Async]
string get_address();

[Throws=Error, Async]
string send_transactions(sequence<Transaction> transactions);

[Throws=Error, Async]
string wait_for_user_operation_receipt(string user_operation_hash);
};
135 changes: 135 additions & 0 deletions crates/kotlin-ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use yttrium::config::Config;
use yttrium::config::Endpoint;
use yttrium::config::Endpoints;
use yttrium::{
account_client::{AccountClient as YAccountClient, SignerType},
private_key_service::PrivateKeyService,
sign_service::address_from_string,
transaction::Transaction as YTransaction,
};

#[derive(uniffi::Object)]
pub struct AccountClient {
pub owner_address: String,
pub chain_id: u64,
account_client: YAccountClient,
}

pub struct AccountClientConfig {
pub owner_address: String,
pub chain_id: u64,
pub config: Config,
pub signer_type: String,
pub safe: bool,
pub private_key: String,
}

pub struct Transaction {
pub to: String,
pub value: String,
pub data: String,
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Unknown {0}")]
Unknown(String),
}

#[uniffi::export(async_runtime = "tokio")]
impl AccountClient {
#[uniffi::constructor]
pub fn new(config: AccountClientConfig) -> Self {
let owner_address = config.owner_address.clone();
let signer_type = config.signer_type.clone();
let signer = SignerType::from(signer_type).unwrap();
let account_client = match signer {
SignerType::PrivateKey => {
let private_key_fn =
Box::new(move || Ok(config.private_key.clone()));
let owner = address_from_string(&owner_address).unwrap();
let service = PrivateKeyService::new(private_key_fn, owner);
YAccountClient::new_with_private_key_service(
config.owner_address.clone(),
config.chain_id,
config.config,
service,
config.safe,
)
}
SignerType::Native => todo!(),
};

Self {
owner_address: config.owner_address.clone(),
chain_id: config.chain_id,
account_client,
}
}

pub fn chain_id(&self) -> u64 {
self.chain_id
}

pub async fn get_address(&self) -> Result<String, Error> {
self.account_client
.get_address()
.await
.map_err(|e| Error::Unknown(e.to_string()))
}

pub async fn send_transactions(
&self,
transactions: Vec<Transaction>,
) -> Result<String, Error> {
let ytransactions: Vec<YTransaction> =
transactions.into_iter().map(YTransaction::from).collect();

Ok(self
.account_client
.send_transactions(ytransactions)
.await
.map_err(|e| Error::Unknown(e.to_string()))?
.to_string())
}

pub fn sign_message_with_mnemonic(
&self,
message: String,
mnemonic: String,
) -> Result<String, Error> {
self.account_client
.sign_message_with_mnemonic(message, mnemonic)
.map_err(|e| Error::Unknown(e.to_string()))
}

pub async fn wait_for_user_operation_receipt(
&self,
user_operation_hash: String,
) -> Result<String, Error> {
self.account_client
.wait_for_user_operation_receipt(
user_operation_hash.parse().map_err(|e| {
Error::Unknown(format!("Parsing user_operation_hash: {e}"))
})?,
)
.await
.iter()
.map(serde_json::to_string)
.collect::<Result<String, serde_json::Error>>()
.map_err(|e| Error::Unknown(e.to_string()))
}
}

uniffi::include_scaffolding!("yttrium");

impl From<Transaction> for YTransaction {
fn from(transaction: Transaction) -> Self {
YTransaction::new_from_strings(
transaction.to,
transaction.value,
transaction.data,
)
.unwrap()
}
}
38 changes: 38 additions & 0 deletions crates/kotlin-ffi/src/yttrium.udl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace yttrium {

};

[Error]
enum Error {
"Unknown"
};

dictionary Endpoint {
string api_key;
string base_url;
};

dictionary Endpoints {
Endpoint rpc;
Endpoint bundler;
Endpoint paymaster;
};

dictionary Config {
Endpoints endpoints;
};

dictionary AccountClientConfig {
string owner_address;
u64 chain_id;
Config config;
string signer_type;
boolean safe;
string private_key;
};

dictionary Transaction {
string to;
string value;
string data;
};
4 changes: 4 additions & 0 deletions crates/kotlin-ffi/uniffi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[bindings.kotlin]
package_name = "com.reown.kotlin.ffi.yttrium"
android = true
android_cleaner = true
2 changes: 1 addition & 1 deletion test/scripts/forked_state/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
restart: unless-stopped
ports: ["8545:8545"]
entrypoint: [ "anvil", "--fork-url", "https://gateway.tenderly.co/public/sepolia", "--host", "0.0.0.0", "--block-time", "0.1", "--gas-price", "1", "--silent", "--hardfork", "prague" ]
platform: linux/amd64/v8
platform: linux/amd64

mock-paymaster:
# image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
Expand Down
2 changes: 1 addition & 1 deletion test/scripts/local_infra/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
image: ghcr.io/foundry-rs/foundry:nightly-f6208d8db68f9acbe4ff8cd76958309efb61ea0b
ports: ["8545:8545"]
entrypoint: ["anvil", "--host", "0.0.0.0", "--block-time", "0.1", "--silent"]
platform: linux/amd64/v8
platform: linux/amd64

mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
Expand Down