Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
8e54d34
spike: add kotlin
joe-p May 7, 2025
5e597cf
wip: add android project
joe-p May 7, 2025
503ccd5
feat: add in library module
michaeltchuang May 14, 2025
f0f7677
add build scripts section
joe-p May 16, 2025
3cf8657
use cargo ndk
joe-p Jun 4, 2025
9587d18
fix: use working copy of transact rust function
michaeltchuang Jun 4, 2025
e6ba286
fix: clean up code
michaeltchuang Jun 4, 2025
6a1aea2
ci: add in mavenLocal publish
michaeltchuang Jun 5, 2025
f157dc5
Merge branch 'main' into spike/kotlin
joe-p Jun 24, 2025
d9a75e2
wip: add kotlin build script
joe-p Jun 24, 2025
308e03b
chore: update README
joe-p Jun 24, 2025
5f03448
chore: update gitignore
joe-p Jun 24, 2025
577b8df
Merge branch 'main' into spike/kotlin
joe-p Sep 17, 2025
82ac644
fix: build errors from latest main changes
michaeltchuang Sep 17, 2025
2ecf988
Merge pull request #6 from michaeltchuang/spike/kotlin
joe-p Sep 19, 2025
d6c122e
fix: replace message with error_msg for kotlin
joe-p Sep 19, 2025
6490bcf
fix: change sample app function reference
michaeltchuang Sep 19, 2025
f2827b3
Merge pull request #7 from michaeltchuang/spike/kotlin
joe-p Sep 19, 2025
c7112db
build: rm kotlin out dir before generating kotlin file
joe-p Sep 19, 2025
889a07f
wip: format swift
joe-p Sep 25, 2025
2643459
wip: swift fix build issues
joe-p Sep 25, 2025
a8dba73
wip: update swift tests
joe-p Sep 25, 2025
8bcd074
wip: new project dir
joe-p Nov 13, 2025
0c09642
chore: fix script
joe-p Nov 13, 2025
af47c41
Merge branch 'main' into spike/kotlin
joe-p Nov 13, 2025
5aa70da
chore: fix merge issues
joe-p Nov 13, 2025
d8a592e
docs: typo
joe-p Nov 13, 2025
e3912aa
chore: rebuild kt
joe-p Nov 13, 2025
22719b7
ci: kotlin ci
joe-p Nov 13, 2025
507817d
ci: specify ndk
joe-p Nov 13, 2025
8dbf107
ci: use sh -c
joe-p Nov 13, 2025
49a7036
ci: only publish on push
joe-p Nov 13, 2025
978793e
ci: remove redundant AAR step
joe-p Nov 13, 2025
919f6c8
chore: rename kotlin to android
joe-p Nov 13, 2025
e9a0056
ci: add test step
joe-p Nov 13, 2025
e6ff7b9
ci: set jdk to 21
joe-p Nov 13, 2025
5800e17
ci: ensure JNA can find libs
joe-p Nov 13, 2025
7690d36
wip: try android emulator test
joe-p Nov 13, 2025
e7d57ed
wip: specify api level
joe-p Nov 13, 2025
e135a9b
wip: specify arch
joe-p Nov 14, 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
64 changes: 64 additions & 0 deletions .github/workflows/android_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Android CI

on:
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
android:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v5

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'

- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.85.0
targets: aarch64-linux-android, arm-linux-androideabi, armv7-linux-androideabi, i686-linux-android, thumbv7neon-linux-androideabi, x86_64-linux-android

- name: Install Cargo-NDK
run: cargo install cargo-ndk@^3.5

- name: Install Android ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: r27d

- name: Build AAR
run: cargo pkg transact aar

- name: Host Test
working-directory: packages/android/algokit_transact
run: ./gradlew test

- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Android Test
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
arch: x86_64
script: ./gradlew connectedAndroidTest
working-directory: packages/android/algokit_transact

- name: Archive Build Output
uses: actions/upload-artifact@v4
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
with:
name: algokit_transact.aar
path: packages/android/algokit_transact/build/outputs/aar/algokit_transact-release.aar
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/target
*.dylib
*.a
*.so
.zed/
.venv/
crates/**/dist/
Expand All @@ -7,9 +10,18 @@ node_modules/
.dmypy.json
.build
.bin/
.vscode/
.DS_Store
*.idea
wheelhouse/

# Android/Kotlin
.gradle/
build/
*.iml
local.properties
gradle-wrapper.jar

# Insta snapshot testing
*.snap.new
*.pending-snap
Expand All @@ -22,6 +34,7 @@ AGENTS.md
.opencode/
.references/
.kiro/
.kotlin/

packages/python/**/*algokit_*.py
packages/python**/*.dll
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ The exported interfaces should be purely functional without any state owned by R

## Contributing

## Build Scripts

`cargo pkg` will run the build script. For example: `cargo pkg transact kt` builds `algokit_transact` for `kotlin`.

The scripts are defined in [tools/build_pkgs] for each language.

### Learning Resources

If you are new to Rust or UniFFI, check out the [learning resources document](./docs/contributing/learning_resources.md)
Expand Down
10 changes: 5 additions & 5 deletions crates/algokit_transact/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ impl FromStr for Address {
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != ALGORAND_ADDRESS_LENGTH {
return Err(AlgoKitTransactError::InvalidAddress {
message: "Algorand address must be exactly 58 characters".into(),
err_msg: "Algorand address must be exactly 58 characters".into(),
});
}
let decoded_address = base32::decode(base32::Alphabet::Rfc4648 { padding: false }, s)
.ok_or_else(|| AlgoKitTransactError::InvalidAddress {
message: "Invalid base32 encoding for Algorand address".into(),
err_msg: "Invalid base32 encoding for Algorand address".into(),
})?;

// Although this is called public key (and it actually is when the account is a `KeyPairAccount`),
Expand All @@ -80,18 +80,18 @@ impl FromStr for Address {
[..ALGORAND_PUBLIC_KEY_BYTE_LENGTH]
.try_into()
.map_err(|_| AlgoKitTransactError::InvalidAddress {
message: "Could not decode address into 32-byte public key".to_string(),
err_msg: "Could not decode address into 32-byte public key".to_string(),
})?;
let checksum: [u8; ALGORAND_CHECKSUM_BYTE_LENGTH] = decoded_address
[ALGORAND_PUBLIC_KEY_BYTE_LENGTH..]
.try_into()
.map_err(|_| AlgoKitTransactError::InvalidAddress {
message: "Could not get 4-byte checksum from decoded address".to_string(),
err_msg: "Could not get 4-byte checksum from decoded address".to_string(),
})?;

if pub_key_to_checksum(&pub_key) != checksum {
return Err(AlgoKitTransactError::InvalidAddress {
message: "Checksum is invalid".to_string(),
err_msg: "Checksum is invalid".to_string(),
});
}
Ok(Address(pub_key))
Expand Down
16 changes: 8 additions & 8 deletions crates/algokit_transact/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ pub enum AlgoKitTransactError {
#[snafu(display("Error ocurred during msgpack decoding: {source}"))]
MsgpackDecodingError { source: rmpv::decode::Error },

#[snafu(display("Unknown transaction type: {message}"))]
UnknownTransactionType { message: String },
#[snafu(display("Unknown transaction type: {err_msg}"))]
UnknownTransactionType { err_msg: String },

#[snafu(display("{message}"))]
InputError { message: String },
#[snafu(display("{err_msg}"))]
InputError { err_msg: String },

#[snafu(display("{message}"))]
InvalidAddress { message: String },
#[snafu(display("{err_msg}"))]
InvalidAddress { err_msg: String },

#[snafu(display("Invalid multisig signature: {message}"))]
InvalidMultisigSignature { message: String },
#[snafu(display("Invalid multisig signature: {err_msg}"))]
InvalidMultisigSignature { err_msg: String },
}

impl From<rmp_serde::encode::Error> for AlgoKitTransactError {
Expand Down
14 changes: 7 additions & 7 deletions crates/algokit_transact/src/multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ impl MultisigSignature {
) -> Result<Self, AlgoKitTransactError> {
if version == 0 {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Version cannot be zero".to_string(),
err_msg: "Version cannot be zero".to_string(),
});
}
if subsignatures.is_empty() {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Subsignatures cannot be empty".to_string(),
err_msg: "Subsignatures cannot be empty".to_string(),
});
}
if threshold == 0 || threshold as usize > subsignatures.len() {
return Err(AlgoKitTransactError::InvalidMultisigSignature { message: "Threshold must be greater than zero and less than or equal to the number of sub-signers".to_string() });
return Err(AlgoKitTransactError::InvalidMultisigSignature { err_msg:"Threshold must be greater than zero and less than or equal to the number of sub-signers".to_string() });
}
Ok(Self {
version,
Expand Down Expand Up @@ -138,7 +138,7 @@ impl MultisigSignature {
}
if !found {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Address not found in multisig signature".to_string(),
err_msg: "Address not found in multisig signature".to_string(),
});
}

Expand All @@ -161,17 +161,17 @@ impl MultisigSignature {
pub fn merge(&self, other: &Self) -> Result<Self, AlgoKitTransactError> {
if self.version != other.version {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Cannot merge multisig signatures with different versions".to_string(),
err_msg: "Cannot merge multisig signatures with different versions".to_string(),
});
}
if self.threshold != other.threshold {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Cannot merge multisig signatures with different thresholds".to_string(),
err_msg: "Cannot merge multisig signatures with different thresholds".to_string(),
});
}
if self.participants() != other.participants() {
return Err(AlgoKitTransactError::InvalidMultisigSignature {
message: "Cannot merge multisig signatures with different participants".to_string(),
err_msg: "Cannot merge multisig signatures with different participants".to_string(),
});
}

Expand Down
2 changes: 1 addition & 1 deletion crates/algokit_transact/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub trait AlgorandMsgpack: Serialize + for<'de> Deserialize<'de> {
fn decode(bytes: &[u8]) -> Result<Self, AlgoKitTransactError> {
if bytes.is_empty() {
return Err(AlgoKitTransactError::InputError {
message: "attempted to decode 0 bytes".to_string(),
err_msg: "attempted to decode 0 bytes".to_string(),
});
}

Expand Down
4 changes: 2 additions & 2 deletions crates/algokit_transact/src/transactions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl Transaction {
if let Some(max_fee) = request.max_fee {
if calculated_fee > max_fee {
return Err(AlgoKitTransactError::InputError {
message: format!(
err_msg: format!(
"Transaction fee {} µALGO is greater than max fee {} µALGO",
calculated_fee, max_fee
),
Expand Down Expand Up @@ -230,7 +230,7 @@ impl AlgorandMsgpack for SignedTransaction {
Ok(stxn)
}
_ => Err(AlgoKitTransactError::InputError {
message: format!(
err_msg: format!(
"expected signed transaction to be a map, but got a: {:#?}",
value.type_id()
),
Expand Down
2 changes: 1 addition & 1 deletion crates/algokit_transact/src/transactions/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ mod tests {
let msg = format!("{}", err);
assert!(
msg == "Transaction fee 2470 µALGO is greater than max fee 1000 µALGO",
"Unexpected error message: {}",
"Unexpected error err_msg:{}",
msg
);
}
Expand Down
8 changes: 4 additions & 4 deletions crates/algokit_transact/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub fn sort_msgpack_value(value: rmpv::Value) -> Result<rmpv::Value, AlgoKitTran
rmpv::Value::Binary(bytes) => binary_entries.push((bytes, sorted_v)),
_ => {
return Err(AlgoKitTransactError::InputError {
message: "Unsupported MessagePack map key type; only integer, string, and binary keys are supported".to_string(),
err_msg: "Unsupported MessagePack map key type; only integer, string, and binary keys are supported".to_string(),
})
}
}
Expand Down Expand Up @@ -286,13 +286,13 @@ pub fn hash(bytes: &Vec<u8>) -> Byte32 {
pub fn compute_group(txs: &[Transaction]) -> Result<Byte32, AlgoKitTransactError> {
if txs.is_empty() {
return Err(AlgoKitTransactError::InputError {
message: String::from("Transaction group size cannot be 0"),
err_msg: String::from("Transaction group size cannot be 0"),
});
}

if txs.len() > MAX_TX_GROUP_SIZE {
return Err(AlgoKitTransactError::InputError {
message: format!(
err_msg: format!(
"Transaction group size exceeds the max limit of {}",
MAX_TX_GROUP_SIZE
),
Expand All @@ -304,7 +304,7 @@ pub fn compute_group(txs: &[Transaction]) -> Result<Byte32, AlgoKitTransactError
.map(|tx| {
if tx.header().group.is_some() {
return Err(AlgoKitTransactError::InputError {
message: "Transactions must not already be grouped".to_string(),
err_msg: "Transactions must not already be grouped".to_string(),
});
}
tx.id_raw()
Expand Down
Loading
Loading