diff --git a/.github/workflows/publish-android.yaml b/.github/workflows/publish-android.yaml
new file mode 100644
index 0000000..bca9a77
--- /dev/null
+++ b/.github/workflows/publish-android.yaml
@@ -0,0 +1,26 @@
+name: Publish Android
+on:
+ release:
+ types: [ created ]
+jobs:
+ publish-package:
+ runs-on: [ "macos-14" ]
+ env:
+ TERM: xterm
+ GITHUB_TOKEN: ${{ secrets.git_pass }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Build and Test Android
+ run: |
+ brew install protobuf
+ cargo clean
+ make clean-mobile-build
+ make test-android
+ - name: Publish Package
+ run: |
+ export LIB_VERSION=${GITHUB_REF#refs/tags/}
+ echo "Publishing version $LIB_VERSION"
+ make publish-android
\ No newline at end of file
diff --git a/.github/workflows/publish-ios.yaml b/.github/workflows/publish-ios.yaml
new file mode 100644
index 0000000..1d7f824
--- /dev/null
+++ b/.github/workflows/publish-ios.yaml
@@ -0,0 +1,91 @@
+name: Publish iOS
+on:
+ release:
+ types: [ created ]
+permissions:
+ contents: write
+jobs:
+ upload-xcframework:
+ runs-on: [ "macos-14" ]
+ env:
+ TERM: xterm
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Build and Test Framework
+ run: |
+ brew install protobuf
+ cargo clean
+ make clean-mobile-build
+ make test-ios
+ - name: Generate checksum
+ run: |
+ swift package compute-checksum packages/kos-mobile/ios/XCFrameworks/KOSMobile.xcframework.zip > checksum.txt
+ - name: Upload checksum
+ uses: actions/upload-artifact@v4
+ with:
+ name: checksum
+ path: checksum.txt
+ - name: Upload Release Asset
+ uses: softprops/action-gh-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.git_pass }}
+ with:
+ name: ${{ github.event.release.name }}
+ files: ./packages/kos-mobile/ios/XCFrameworks/KOSMobile.xcframework.zip
+
+ create-ios-release:
+ needs: upload-xcframework
+ runs-on: [ "macos-14" ]
+ env:
+ TERM: xterm
+ GITHUB_TOKEN: ${{ secrets.git_pass }}
+ steps:
+ - name: Checkout kos-rs-xcframework
+ uses: actions/checkout@v4
+ with:
+ repository: klever-io/kos-rs-xcframework
+ token: ${{ secrets.git_pass }}
+ - name: Download checksum
+ uses: actions/download-artifact@v4
+ with:
+ name: checksum
+ path: .
+ - name: Update Swift Package Url and Checksum
+ run: |
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ export NEW_URL="https://github.com/klever-io/kos-rs/releases/download/$TAG_NAME/KOSMobile.xcframework.zip"
+ export NEW_CHECKSUM=$(cat checksum.txt)
+ echo "URL: $NEW_URL"
+ echo "Checksum: $NEW_CHECKSUM"
+ chmod +x ./update_package.sh
+ ./update_package.sh
+ - name: Commit Changes
+ run: |
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "actions@github.com"
+ git add -f Package.swift
+ git commit -m "Update package.swift"
+ git push origin main
+ - name: Create Release
+ run: |
+ brew install jq
+ TAG_NAME=${GITHUB_REF#refs/tags/}
+ RELEASE_NAME="${{ github.event.release.name }}"
+ RELEASE_BODY="${{ github.event.release.body }}"
+ PRERELEASE=${{ github.event.release.prerelease }}
+ ESCAPED_BODY=$(echo "${RELEASE_BODY}" | jq -Rsa .)
+ curl -f -X POST \
+ -H "Authorization: token $GITHUB_TOKEN" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "tag_name": "'"${TAG_NAME}"'",
+ "target_commitish": "main",
+ "name": "'"${RELEASE_NAME}"'",
+ "body": '"${ESCAPED_BODY}"',
+ "draft": false,
+ "prerelease": '"${PRERELEASE}"'
+ }' \
+ https://api.github.com/repos/klever-io/kos-rs-xcframework/releases
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index ea4f4b7..4e59e0b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -61,6 +61,55 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "anstream"
+version = "0.6.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "anyhow"
version = "1.0.86"
@@ -73,6 +122,47 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+[[package]]
+name = "askama"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
+dependencies = [
+ "askama_parser",
+ "basic-toml",
+ "mime",
+ "mime_guess",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 2.0.66",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
+
+[[package]]
+name = "askama_parser"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
+dependencies = [
+ "nom",
+]
+
[[package]]
name = "async-stream"
version = "0.3.5"
@@ -161,12 +251,30 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+[[package]]
+name = "basic-toml"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "bech32"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "bitcoin"
version = "0.30.2"
@@ -279,6 +387,38 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
+[[package]]
+name = "camino"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
[[package]]
name = "cbc"
version = "0.1.2"
@@ -329,6 +469,46 @@ dependencies = [
"inout",
]
+[[package]]
+name = "clap"
+version = "4.5.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+
[[package]]
name = "cobs"
version = "0.2.3"
@@ -393,6 +573,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+[[package]]
+name = "colorchoice"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
@@ -841,6 +1027,15 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs-err"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
+dependencies = [
+ "autocfg",
+]
+
[[package]]
name = "funty"
version = "2.0.0"
@@ -1004,6 +1199,17 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "goblin"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
+dependencies = [
+ "log",
+ "plain",
+ "scroll",
+]
+
[[package]]
name = "group"
version = "0.13.0"
@@ -1408,6 +1614,12 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
[[package]]
name = "itertools"
version = "0.10.5"
@@ -1526,6 +1738,21 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "kos-mobile"
+version = "0.1.0"
+dependencies = [
+ "hex",
+ "kos-crypto",
+ "kos-proto",
+ "kos-sdk",
+ "kos-types",
+ "kos-utils",
+ "lazy_static",
+ "thiserror",
+ "uniffi",
+]
+
[[package]]
name = "kos-proto"
version = "0.1.0"
@@ -1567,6 +1794,7 @@ dependencies = [
"kos-proto",
"kos-types",
"kos-utils",
+ "lazy_static",
"log",
"pbjson",
"pbjson-types",
@@ -1663,6 +1891,22 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+[[package]]
+name = "mime_guess"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
[[package]]
name = "miniz_oxide"
version = "0.7.3"
@@ -1713,6 +1957,16 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
[[package]]
name = "num-bigint"
version = "0.4.5"
@@ -1876,6 +2130,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
[[package]]
name = "pbjson"
version = "0.5.1"
@@ -1997,6 +2257,12 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
[[package]]
name = "png"
version = "0.17.13"
@@ -2587,6 +2853,26 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "scroll"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
+dependencies = [
+ "scroll_derive",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.66",
+]
+
[[package]]
name = "sec1"
version = "0.7.3"
@@ -2650,6 +2936,9 @@ name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+dependencies = [
+ "serde",
+]
[[package]]
name = "send_wrapper"
@@ -2759,6 +3048,12 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -2774,6 +3069,12 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+[[package]]
+name = "smawk"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
+
[[package]]
name = "socket2"
version = "0.5.7"
@@ -2815,6 +3116,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
[[package]]
name = "strum"
version = "0.25.0"
@@ -2910,6 +3217,15 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "textwrap"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
+dependencies = [
+ "smawk",
+]
+
[[package]]
name = "thiserror"
version = "1.0.61"
@@ -3028,6 +3344,15 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "toml_datetime"
version = "0.6.6"
@@ -3145,6 +3470,15 @@ dependencies = [
"static_assertions",
]
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
[[package]]
name = "unicode-bidi"
version = "0.3.15"
@@ -3166,6 +3500,137 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "uniffi"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2db87def739fe4183947f8419d572d1849a4a09355eba4e988a2105cfd0ac6a7"
+dependencies = [
+ "anyhow",
+ "camino",
+ "cargo_metadata",
+ "clap",
+ "uniffi_bindgen",
+ "uniffi_build",
+ "uniffi_core",
+ "uniffi_macros",
+]
+
+[[package]]
+name = "uniffi_bindgen"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a112599c9556d1581e4a3d72019a74c2c3e122cc27f4af12577a429c4d5e614"
+dependencies = [
+ "anyhow",
+ "askama",
+ "camino",
+ "cargo_metadata",
+ "fs-err",
+ "glob",
+ "goblin",
+ "heck 0.5.0",
+ "once_cell",
+ "paste",
+ "serde",
+ "textwrap",
+ "toml",
+ "uniffi_meta",
+ "uniffi_udl",
+]
+
+[[package]]
+name = "uniffi_build"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2b12684401d2a8508ca9c72a95bbc45906417e42fc80942abaf033bbf01aa33"
+dependencies = [
+ "anyhow",
+ "camino",
+ "uniffi_bindgen",
+]
+
+[[package]]
+name = "uniffi_checksum_derive"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a22dbe67c1c957ac6e7611bdf605a6218aa86b0eebeb8be58b70ae85ad7d73dc"
+dependencies = [
+ "quote",
+ "syn 2.0.66",
+]
+
+[[package]]
+name = "uniffi_core"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a0c35aaad30e3a9e6d4fe34e358d64dbc92ee09045b48591b05fc9f12e0905b"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "camino",
+ "log",
+ "once_cell",
+ "paste",
+ "static_assertions",
+]
+
+[[package]]
+name = "uniffi_macros"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db66474c5c61b0f7afc3b4995fecf9b72b340daa5ca0ef3da7778d75eb5482ea"
+dependencies = [
+ "bincode",
+ "camino",
+ "fs-err",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 2.0.66",
+ "toml",
+ "uniffi_meta",
+]
+
+[[package]]
+name = "uniffi_meta"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d898893f102e0e39b8bcb7e3d2188f4156ba280db32db9e8af1f122d057e9526"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "siphasher",
+ "uniffi_checksum_derive",
+]
+
+[[package]]
+name = "uniffi_testing"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6aa4f0cf9d12172d84fc00a35a6c1f3522b526daad05ae739f709f6941b9b6"
+dependencies = [
+ "anyhow",
+ "camino",
+ "cargo_metadata",
+ "fs-err",
+ "once_cell",
+]
+
+[[package]]
+name = "uniffi_udl"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b044e9c519e0bb51e516ab6f6d8f4f4dcf900ce30d5ad07c03f924e2824f28e"
+dependencies = [
+ "anyhow",
+ "textwrap",
+ "uniffi_meta",
+ "uniffi_testing",
+ "weedle2",
+]
+
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -3199,6 +3664,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -3371,6 +3842,15 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "weedle2"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e"
+dependencies = [
+ "nom",
+]
+
[[package]]
name = "which"
version = "4.4.2"
diff --git a/Cargo.toml b/Cargo.toml
index 5fdd5c9..d3c3b5a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@ members = [
"packages/kos",
"packages/kos-crypto",
"packages/kos-sdk",
+ "packages/kos-mobile",
]
# This makes the compiled code faster and smaller, but it makes compiling slower,
# so it's only enabled in release mode.
@@ -41,6 +42,8 @@ pbjson = { version = "0.5", git = "https://github.com/klever-io/pbjson" }
pbjson-types = { version = "0.5", git = "https://github.com/klever-io/pbjson" }
pbjson-build = { version = "0.5", git = "https://github.com/klever-io/pbjson" }
+uniffi = { version = "0.28.1"}
+
reqwest = { version = "0.12", default-features = false, feature = ["rustls-tls", "blocking", "json"] }
wasm-bindgen = "0.2"
enum_delegate = "0.2"
@@ -48,9 +51,11 @@ serde = { version = "1.0", default-features = false }
serde_json = "1.0"
log = "0.4"
+lazy_static = "1.4.0"
+thiserror = "1.0"
kos-types = { version = "0.1.0", path = "./packages/kos-types", default-features = false }
kos-crypto = { version = "0.1.0", path = "./packages/kos-crypto", default-features = false }
kos-sdk = { version = "0.1.0", path = "./packages/kos-sdk", default-features = false }
kos-utils = { version = "0.1.0", path = "./packages/kos-utils", default-features = false }
-kos-proto = { version = "0.1.0", path = "./packages/kos-proto", default-features = false }
+kos-proto = { version = "0.1.0", path = "./packages/kos-proto", default-features = false }
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 060dd1e..feadc9b 100644
--- a/Makefile
+++ b/Makefile
@@ -35,3 +35,27 @@ webpack:
webpack-npm:
wasm-pack build --scope klever --target bundler --release --out-name index --out-dir ../../demo/kos ./packages/kos
+
+clean-mobile-build:
+ cd packages/kos-mobile && ./build_clean.sh
+
+build-android:
+ cd packages/kos-mobile && ./build_android.sh
+
+publish-android:
+ cd packages/kos-mobile/android && ./gradlew lib:publishKOSPublicationToGithubPackagesRepository
+
+build-ios:
+ cd packages/kos-mobile && ./build_ios.sh
+
+test-ios: build-ios
+ cd packages/kos-mobile/ios/framework/KOSMobile && xcodebuild \
+ -project KOSMobile.xcodeproj \
+ -scheme KOSMobile \
+ -sdk iphonesimulator \
+ -destination 'platform=iOS Simulator,OS=17.2,name=iPhone 15 Pro' \
+ CODE_SIGNING_ALLOWED=NO \
+ test
+
+test-android: build-android
+ cd packages/kos-mobile/android && ./gradlew lib:testDebugUnitTest
\ No newline at end of file
diff --git a/packages/kos-mobile/Cargo.toml b/packages/kos-mobile/Cargo.toml
new file mode 100644
index 0000000..6331fd1
--- /dev/null
+++ b/packages/kos-mobile/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "kos-mobile"
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
+version.workspace = true
+
+[lib]
+crate-type = ["cdylib", "staticlib"]
+
+[[bin]]
+name = "uniffi-bindgen"
+path = "src/bin/uniffi-bindgen.rs"
+
+[dependencies]
+kos-types = { workspace = true, features = ["serde"] }
+kos-crypto = { workspace = true }
+kos-proto = { workspace = true }
+kos-utils = { workspace = true }
+kos-sdk = { workspace = true, features = ["serde"] }
+
+hex = { workspace = true }
+lazy_static = { workspace = true }
+thiserror = { workspace = true }
+
+uniffi = { workspace = true, features = ["cli"] }
+
+[build-dependencies]
+uniffi = { workspace = true, features = ["build"] }
\ No newline at end of file
diff --git a/packages/kos-mobile/android/.gitignore b/packages/kos-mobile/android/.gitignore
new file mode 100644
index 0000000..7ac67d5
--- /dev/null
+++ b/packages/kos-mobile/android/.gitignore
@@ -0,0 +1,58 @@
+# Build and compilation directories
+build/
+.build
+out/
+.gradle/
+.idea/
+.kotlin
+docs
+
+# Local IntelliJ IDEA configuration files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# Kotlin Native user-specific local file
+.konan/
+
+# Gradle cache folder
+.gradle/
+!gradle-wrapper.jar
+
+# Log files
+*.log
+
+# Temporary files
+*.tmp
+*.tmp.*
+
+# Operating system specific files
+.DS_Store
+Thumbs.db
+
+# JAR output folder
+libs/
+
+# Android-specific ignores
+*.apk
+*.ap_
+*.aab
+bin/
+gen/
+local.properties
+
+# Keystore files for Android signing
+*.jks
+*.keystore
+
+# Google Services (exclude your google-services.json)
+/google-services.json
+
+# specific
+
+ndk
+openssl
+openssl-libs
+lib/src/main/kotlin/uniffi
+lib/src/main/jniLibs
\ No newline at end of file
diff --git a/packages/kos-mobile/android/build.gradle.kts b/packages/kos-mobile/android/build.gradle.kts
new file mode 100644
index 0000000..e037951
--- /dev/null
+++ b/packages/kos-mobile/android/build.gradle.kts
@@ -0,0 +1,4 @@
+plugins {
+ alias(libs.plugins.android.library) apply false
+ alias(libs.plugins.jetbrains.kotlin.android) apply false
+}
\ No newline at end of file
diff --git a/packages/kos-mobile/android/gradle.properties b/packages/kos-mobile/android/gradle.properties
new file mode 100644
index 0000000..20e2a01
--- /dev/null
+++ b/packages/kos-mobile/android/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/packages/kos-mobile/android/gradle/libs.versions.toml b/packages/kos-mobile/android/gradle/libs.versions.toml
new file mode 100644
index 0000000..e4f5e34
--- /dev/null
+++ b/packages/kos-mobile/android/gradle/libs.versions.toml
@@ -0,0 +1,13 @@
+[versions]
+agp = "8.3.1"
+kotlin = "1.9.23"
+jna = "5.13.0"
+junit = "4.13.2"
+
+[libraries]
+java-jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+
+[plugins]
+android-library = { id = "com.android.library", version.ref = "agp" }
+jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
\ No newline at end of file
diff --git a/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..2f465c7
--- /dev/null
+++ b/packages/kos-mobile/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Aug 19 09:00:38 BRT 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/packages/kos-mobile/android/gradlew b/packages/kos-mobile/android/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/packages/kos-mobile/android/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/packages/kos-mobile/android/gradlew.bat b/packages/kos-mobile/android/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/packages/kos-mobile/android/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/packages/kos-mobile/android/lib/build.gradle.kts b/packages/kos-mobile/android/lib/build.gradle.kts
new file mode 100644
index 0000000..9fd6331
--- /dev/null
+++ b/packages/kos-mobile/android/lib/build.gradle.kts
@@ -0,0 +1,84 @@
+import java.net.URI
+import java.util.Properties
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.jetbrains.kotlin.android)
+ id("maven-publish")
+}
+
+group = "io.klever"
+version = System.getenv("LIB_VERSION")?.removePrefix("v") ?: "LOCAL"
+
+android {
+ namespace = "uniffi.kos_mobile"
+ compileSdk = 34
+ defaultConfig {
+ minSdk = 27
+ }
+ buildTypes {
+ release { isMinifyEnabled = false }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.java.jna)
+ testImplementation(libs.junit)
+}
+
+publishing {
+ repositories {
+ maven {
+ name = "GithubPackages"
+ url = URI.create("https://maven.pkg.github.com/klever-io/kos-rs")
+ credentials {
+ username = ""
+ password = (System.getenv("GITHUB_TOKEN")
+ ?: (getLocalProperties()["git.token"] as? String?)
+ ?: "")
+ }
+ }
+ }
+
+ publications {
+ create("KOS", MavenPublication::class.java) {
+ artifactId = "kos-mobile"
+ afterEvaluate {
+ artifact(tasks.getByName("bundleReleaseAar"))
+ }
+ }
+ }
+}
+
+tasks.register("copyKOSMobileDarwinAarch64LibForTestDebugUnitTest", Copy::class) {
+ dependsOn("processDebugUnitTestJavaRes")
+ from("$rootDir/lib/src/main/jniLibs/darwin-aarch64") {
+ include("libkos_mobile.dylib")
+ }
+ into("$rootDir/lib/build/tmp/kotlin-classes/debugUnitTest/darwin-aarch64")
+}
+
+afterEvaluate {
+ tasks.named("testDebugUnitTest") {
+ dependsOn("copyKOSMobileDarwinAarch64LibForTestDebugUnitTest")
+ }
+}
+
+fun getLocalProperties(): Properties = System.getProperties().apply {
+ try {
+ load(File("${rootDir}${File.separator}local.properties").inputStream())
+ } catch (_: Exception) {
+ }
+}
\ No newline at end of file
diff --git a/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml b/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1d26c87
--- /dev/null
+++ b/packages/kos-mobile/android/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt b/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt
new file mode 100644
index 0000000..0f102b0
--- /dev/null
+++ b/packages/kos-mobile/android/lib/src/test/kotlin/uniffi/kos_mobile/KOSTest.kt
@@ -0,0 +1,68 @@
+package uniffi.kos_mobile
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class KOSTest {
+
+ @Test
+ fun testKOS() {
+
+ val dataToEncrypt = "Hello"
+ val password = "password"
+ val klvChainId = 38
+ val mnemonic =
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+ val klvPk0 = "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d"
+ val klvAddr0 = "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy"
+ val klvPath0 = "m/44'/690'/0'/0'/0'"
+ val klvKey0 = "e41b323a571fd955e09cd41660ff4465c3f44693c87f2faea4a0fc408727c8ea"
+
+ val isValidMnemonicValid = validateMnemonic(mnemonic)
+ assertTrue(isValidMnemonicValid)
+
+ val isInvalidMnemonicValid =
+ validateMnemonic("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon klv")
+ assertFalse(isInvalidMnemonicValid)
+
+ val mnemonic12 = generateMnemonic(12)
+ assertTrue(mnemonic12.split(" ").size == 12)
+
+ val mnemonic24 = generateMnemonic(24)
+ assertTrue(mnemonic24.split(" ").size == 24)
+
+ val gmcEncryptedData = encryptWithGmc(dataToEncrypt, password)
+ assertTrue(gmcEncryptedData.isNotEmpty())
+
+ val cbcEncryptedData = encryptWithCbc(dataToEncrypt, password)
+ assertTrue(cbcEncryptedData.isNotEmpty())
+
+ val cfbEncryptedData = encryptWithCfb(dataToEncrypt, password)
+ assertTrue(cfbEncryptedData.isNotEmpty())
+
+ val gmcDecryptedData = decrypt(gmcEncryptedData, password)
+ assertEquals(dataToEncrypt, gmcDecryptedData)
+
+ val cbcDecryptedData = decrypt(cbcEncryptedData, password)
+ assertEquals(dataToEncrypt, cbcDecryptedData)
+
+ val cfbDecryptedData = decrypt(cfbEncryptedData, password)
+ assertEquals(dataToEncrypt, cfbDecryptedData)
+
+ val walletFromMnemonic = generateWalletFromMnemonic(mnemonic, klvChainId, 0, false)
+ assertEquals(klvChainId, walletFromMnemonic.chainId)
+ assertEquals(klvPk0, walletFromMnemonic.privateKey)
+ assertEquals(klvAddr0, walletFromMnemonic.address)
+ assertEquals(klvPath0, walletFromMnemonic.path)
+ assertEquals(klvKey0, walletFromMnemonic.publicKey)
+
+ val walletFromPk = generateWalletFromPrivateKey(klvChainId, klvPk0)
+ assertEquals(klvChainId, walletFromPk.chainId)
+ assertEquals(klvPk0, walletFromPk.privateKey)
+ assertEquals(klvAddr0, walletFromPk.address)
+ assertEquals("", walletFromPk.path)
+ assertEquals(klvKey0, walletFromPk.publicKey)
+ }
+}
\ No newline at end of file
diff --git a/packages/kos-mobile/android/settings.gradle.kts b/packages/kos-mobile/android/settings.gradle.kts
new file mode 100644
index 0000000..9a6ec3d
--- /dev/null
+++ b/packages/kos-mobile/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+@file:Suppress("UnstableApiUsage")
+
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "kos"
+include(":lib")
+
\ No newline at end of file
diff --git a/packages/kos-mobile/build_android.sh b/packages/kos-mobile/build_android.sh
new file mode 100755
index 0000000..d4dbcfa
--- /dev/null
+++ b/packages/kos-mobile/build_android.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+source build_source.sh
+
+set -o pipefail
+set -e
+
+configure_cargo() {
+ cd "$BUILD_HOME"
+ for i in $(seq 0 $((${#ANDROID_TOOLCHAINS[@]} - 1))); do
+ toolchain="${ANDROID_TOOLCHAINS[i]}"
+ if [ "$toolchain" = "armv7a-linux-androideabi" ]; then
+ toolchain="armv7-linux-androideabi"
+ fi
+ rustup target add $toolchain
+ done
+ export AR="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/llvm-ar"
+ export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/aarch64-linux-android$ANDROID_MIN_API-clang"
+ export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/armv7a-linux-androideabi$ANDROID_MIN_API-clang"
+ export CARGO_TARGET_I686_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/i686-linux-android$ANDROID_MIN_API-clang"
+ export CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/x86_64-linux-android$ANDROID_MIN_API-clang"
+}
+
+assemble_android_lib() {
+ toolchain=$1
+ jni=$2
+ rust_toolchain=$toolchain
+ cd "$BUILD_HOME"
+ if [ "$toolchain" = "armv7a-linux-androideabi" ]; then
+ rust_toolchain="armv7-linux-androideabi"
+ fi
+ export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$toolchain"
+ export CC="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS/bin/$toolchain$ANDROID_MIN_API-clang"
+ log_status "assembling android lib to $toolchain..."
+ cargo build --target $rust_toolchain --release -q
+ export CC=""
+ mkdir -p "$ANDROID_JNI_LIBS_PATH"
+ mkdir -p "$ANDROID_JNI_LIBS_PATH/$jni"
+ cp -f ../../target/$rust_toolchain/release/lib"$PACKAGE_NAME".so "$ANDROID_JNI_LIBS_PATH"/"$jni"
+}
+
+assemble_android_lib_unit_test() {
+ cd "$BUILD_HOME"
+ jni=darwin-aarch64
+ log_status "assembling android test lib..."
+ export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$OS_TOOLCHAIN"
+ cargo build --release -q
+ mkdir -p "$ANDROID_JNI_LIBS_PATH/$jni"
+ cp -f ../../target/release/lib"$PACKAGE_NAME".dylib "$ANDROID_JNI_LIBS_PATH"/$jni
+}
+
+generate_binds() {
+ cd "$BUILD_HOME"
+ log_status "generating android binds..."
+ export OPENSSL_LIB_DIR="$OPENSSL_GENERATED_LIBS_PATH/$OS_TOOLCHAIN"
+ cargo run -q --bin uniffi-bindgen generate --library ../../target/"${ANDROID_TOOLCHAINS[0]}"/release/lib"${PACKAGE_NAME}".so --language kotlin --out-dir android_binds
+ mkdir -p "$ANDROID_GENERATED_BINDS_PATH"
+ cp -f -r android_binds/* "$ANDROID_GENERATED_BINDS_PATH"
+ rm -rf android_binds
+}
+
+clear
+echo -e "${ANDROID}########################################################${NC}"
+echo -e "${ANDROID}######### INITIALIZING ANDROID RUST BUILD ############${NC}"
+echo -e "${ANDROID}########################################################${NC}\n"
+
+configure_android_ndk
+configure_openssl
+configure_cargo
+
+for i in $(seq 0 $((${#ANDROID_ARCHS[@]} - 1))); do
+ assemble_openssl_lib "${ANDROID_ARCHS[i]}" "${ANDROID_TOOLCHAINS[i]}"
+ assemble_android_lib "${ANDROID_TOOLCHAINS[i]}" "${ANDROID_JNI[i]}"
+done
+
+assemble_openssl_lib "$OS_ARCH" "$OS_TOOLCHAIN"
+assemble_android_lib_unit_test
+generate_binds
+
+echo -e "${ANDROID}ANDROID RUST BUILD FINISHED ๐๐๐${NC}"
diff --git a/packages/kos-mobile/build_clean.sh b/packages/kos-mobile/build_clean.sh
new file mode 100755
index 0000000..ae4de35
--- /dev/null
+++ b/packages/kos-mobile/build_clean.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+source build_source.sh
+
+set -o pipefail
+set -e
+
+clear_android() {
+ rm -rf "$ANDROID_JNI_LIBS_PATH"
+ rm -rf "$ANDROID_GENERATED_BINDS_PATH"/uniffi
+}
+
+clear_ios() {
+ cd "$BUILD_HOME"
+ rm -rf ios/XCFrameworks
+ rm -rf ios/binds
+ rm -rf ios/framework/KOSMobile/aarch64-apple-ios
+ rm -rf ios/framework/KOSMobile/aarch64-apple-ios-sim
+}
+
+log_status "cleaning android files..."
+clear_android
+log_status "cleaning iOS files..."
+clear_ios
+
+log_status "clear finished!"
diff --git a/packages/kos-mobile/build_ios.sh b/packages/kos-mobile/build_ios.sh
new file mode 100755
index 0000000..7569ede
--- /dev/null
+++ b/packages/kos-mobile/build_ios.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+source build_source.sh
+
+set -o pipefail
+set -e
+
+configure_cargo() {
+ cd "$BUILD_HOME"
+ for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do
+ rustup target add "${IOS_ARCHS[i]}"
+ done
+}
+
+assemble_ios_lib() {
+ arch=$1
+ cd "$BUILD_HOME"
+ log_status "assembling iOS lib to $arch..."
+ cargo build --target "$arch" --release -q
+ cd ../../target/"$arch"/release
+ mv lib"$PACKAGE_NAME".a "$arch"-lib"$PACKAGE_NAME".a
+}
+
+generate_binds() {
+ cd "$BUILD_HOME/../.."
+ log_status "generating iOS binds..."
+ rm -rf ios/binds
+ cargo run -q --bin uniffi-bindgen generate --library target/"${IOS_ARCHS[0]}"/release/lib"$PACKAGE_NAME".dylib --language swift --out-dir packages/kos-mobile/ios/binds
+}
+
+generate_xcframework() {
+ log_status "generating XCFramework..."
+ cd "$BUILD_HOME"/ios/framework/KOSMobile
+ for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do
+ rm -rf "${IOS_ARCHS[i]}"
+ mkdir -p "${IOS_ARCHS[i]}"/
+ if [ "${IOS_ARCHS[i]}" = "aarch64-apple-ios" ]; then
+ xcodebuild -project KOSMobile.xcodeproj \
+ -scheme KOSMobile \
+ -configuration Release \
+ -sdk iphoneos \
+ -arch arm64 \
+ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
+ SKIP_INSTALL=NO \
+ clean build
+ BUILT_PRODUCTS_DIR=$(xcodebuild -project KOSMobile.xcodeproj \
+ -scheme KOSMobile \
+ -configuration Release \
+ -sdk iphoneos \
+ -arch arm64 \
+ -showBuildSettings |
+ grep "BUILT_PRODUCTS_DIR" |
+ grep -oEi "\/.*")
+ cp -f -r "$BUILT_PRODUCTS_DIR"/* "${IOS_ARCHS[i]}"/
+ elif [ "${IOS_ARCHS[i]}" = "aarch64-apple-ios-sim" ]; then
+ xcodebuild -project KOSMobile.xcodeproj \
+ -scheme KOSMobile \
+ -configuration Release \
+ -sdk iphonesimulator \
+ -arch arm64 \
+ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
+ SKIP_INSTALL=NO \
+ clean build
+ BUILT_PRODUCTS_DIR=$(xcodebuild -project KOSMobile.xcodeproj \
+ -scheme KOSMobile \
+ -configuration Release \
+ -sdk iphonesimulator \
+ -arch arm64 \
+ -showBuildSettings |
+ grep "BUILT_PRODUCTS_DIR" |
+ grep -oEi "\/.*")
+ cp -f -r "$BUILT_PRODUCTS_DIR"/* "${IOS_ARCHS[i]}"/
+ fi
+ done
+ rm -rf ../../XCFrameworks
+ mkdir -p ../../XCFrameworks
+ xcodebuild -create-xcframework \
+ -framework "${IOS_ARCHS[0]}"/KOSMobile.framework \
+ -framework "${IOS_ARCHS[1]}"/KOSMobile.framework \
+ -output ../../XCFrameworks/KOSMobile.xcframework
+ cd ../../XCFrameworks
+ zip -r -y KOSMobile.xcframework.zip KOSMobile.xcframework
+}
+
+clear
+echo -e "${IOS}########################################################${NC}"
+echo -e "${IOS}########### INITIALIZING iOS RUST BUILD ##############${NC}"
+echo -e "${IOS}########################################################${NC}\n"
+
+configure_cargo
+for i in $(seq 0 $((${#IOS_ARCHS[@]} - 1))); do
+ assemble_ios_lib "${IOS_ARCHS[i]}"
+done
+generate_binds
+generate_xcframework
+
+echo -e "${IOS}iOS RUST BUILD FINISHED ๐๐๐${NC}"
diff --git a/packages/kos-mobile/build_source.sh b/packages/kos-mobile/build_source.sh
new file mode 100755
index 0000000..e37d721
--- /dev/null
+++ b/packages/kos-mobile/build_source.sh
@@ -0,0 +1,116 @@
+#!/bin/bash
+
+# build env
+BUILD_HOME=$(pwd)
+OS="darwin-x86_64"
+OS_ARCH="darwin64-arm64-cc"
+OS_TOOLCHAIN="darwin-x86_64"
+ANDROID_PROJECT_PATH="android"
+ANDROID_NDK_PATH="$BUILD_HOME/android/ndk"
+ANDROID_JNI_LIBS_PATH="$BUILD_HOME/$ANDROID_PROJECT_PATH/lib/src/main/jniLibs"
+ANDROID_GENERATED_BINDS_PATH="$BUILD_HOME/$ANDROID_PROJECT_PATH/lib/src/main/kotlin"
+ANDROID_MIN_API="27"
+ANDROID_ARCHS=("android-arm64" "android-arm" "android-x86" "android-x86_64")
+ANDROID_TOOLCHAINS=("aarch64-linux-android" "armv7a-linux-androideabi" "i686-linux-android" "x86_64-linux-android")
+ANDROID_JNI=("arm64-v8a" "armeabi-v7a" "x86" "x86_64")
+OPENSSL_VERSION="openssl-3.2.1"
+OPENSSL_PATH="$BUILD_HOME/android/openssl"
+OPENSSL_GENERATED_LIBS_PATH="$OPENSSL_PATH-libs"
+IOS_ARCHS=("aarch64-apple-ios" "aarch64-apple-ios-sim")
+PACKAGE_NAME="kos_mobile"
+
+# colors
+ANDROID='\033[0;92m'
+IOS='\033[0;97m'
+RED='\033[0;31m'
+GRAY='\033[37m'
+NC='\033[0m'
+
+dir_exists() {
+ if [ ! -d "$1" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+file_exists() {
+ if [ -f "$1" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+log_status() {
+ echo -e "${GRAY}$1${NC}"
+}
+
+log_error() {
+ echo -e "${RED}$1${NC}"
+}
+
+configure_android_ndk() {
+ if ! dir_exists "$ANDROID_NDK_PATH"; then
+ log_status "configuring ndk..."
+ rm -f ndk.dmg
+ log_status "starting ndk download..."
+ curl -0 https://dl.google.com/android/repository/android-ndk-r26b-darwin.dmg --output "$BUILD_HOME"/ndk.dmg
+ hdiutil attach -quiet -nobrowse -noverify -noautoopen "$BUILD_HOME"/ndk.dmg
+ mkdir "$ANDROID_NDK_PATH"
+ log_status "copying ndk files..."
+ cp -r /Volumes/Android\ NDK\ r26b/AndroidNDK10909125.app/Contents/NDK/* "$ANDROID_NDK_PATH"
+ hdiutil detach -quiet /Volumes/Android\ NDK\ r26b/
+ rm ndk.dmg
+ fi
+ export ANDROID_NDK_ROOT="$ANDROID_NDK_PATH"
+}
+
+configure_openssl() {
+ if ! dir_exists "$OPENSSL_PATH"; then
+ log_status "configuring open-ssl..."
+ rm -f "$OPENSSL_VERSION".tar.gz
+ log_status "starting $OPENSSL_VERSION download..."
+ curl -L -o "$OPENSSL_VERSION".tar.gz https://github.com/openssl/openssl/releases/download/"$OPENSSL_VERSION"/"$OPENSSL_VERSION".tar.gz
+ tar xfz "${OPENSSL_VERSION}.tar.gz"
+ mkdir "$OPENSSL_PATH"
+ cp -r "$BUILD_HOME"/"$OPENSSL_VERSION"/* "$OPENSSL_PATH"
+ rm -f "$OPENSSL_VERSION".tar.gz
+ rm -rf "$OPENSSL_VERSION"
+ mkdir -p "$OPENSSL_GENERATED_LIBS_PATH"
+ fi
+ export OPENSSL_DIR=$OPENSSL_PATH
+ export TOOLCHAIN_ROOT="$ANDROID_NDK_PATH/toolchains/llvm/prebuilt/$OS"
+ export SYSROOT="$TOOLCHAIN_ROOT/sysroot"
+ export PATH="$TOOLCHAIN_ROOT/bin:$SYSROOT/usr/local/bin:$PATH"
+}
+
+assemble_openssl_lib() {
+ arch=$1
+ toolchain=$2
+ if ! dir_exists "$OPENSSL_GENERATED_LIBS_PATH/$toolchain"; then
+ cd "$OPENSSL_PATH"
+ log_status "configuring openssl to $toolchain..."
+ if [ "$arch" = "$OS_ARCH" ]; then
+ ./Configure "$arch" no-asm no-shared
+ else
+ export CC="${TOOLCHAIN_ROOT}/bin/$toolchain${ANDROID_MIN_API}-clang"
+ export CXX="${TOOLCHAIN_ROOT}/bin/$toolchain${ANDROID_MIN_API}-clang++"
+ export CXXFLAGS="-fPIC"
+ export CPPFLAGS="-DANDROID -fPIC"
+ ./Configure "$arch" no-asm no-shared -D__ANDROID_API__="$ANDROID_MIN_API"
+ fi
+ log_status "assembling openssl to $toolchain..."
+ make clean -s
+ make -s
+ mkdir -p "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain"
+ cp -f libcrypto.a "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain"
+ cp -f libssl.a "$OPENSSL_GENERATED_LIBS_PATH"/"$toolchain"
+ else
+ log_status "skipping assemble openssl to $toolchain..."
+ fi
+ export CC=""
+ export CXX=""
+ export CXXFLAGS=""
+ export CPPFLAGS=""
+}
diff --git a/packages/kos-mobile/ios/.gitignore b/packages/kos-mobile/ios/.gitignore
new file mode 100644
index 0000000..47b6501
--- /dev/null
+++ b/packages/kos-mobile/ios/.gitignore
@@ -0,0 +1,2 @@
+XCFrameworks
+binds
\ No newline at end of file
diff --git a/packages/kos-mobile/ios/framework/.gitignore b/packages/kos-mobile/ios/framework/.gitignore
new file mode 100644
index 0000000..c1eb49a
--- /dev/null
+++ b/packages/kos-mobile/ios/framework/.gitignore
@@ -0,0 +1,41 @@
+.DS_Store
+/build/
+DerivedData/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+*.xccheckout
+*.moved-aside
+*.xcworkspace
+!default.xcworkspace
+*.plist
+!default.plist
+*.swp
+*.swo
+*~
+*.ipa
+*.dSYM.zip
+*.dSYM
+*.hmap
+*.ipa
+*.xcuserstate
+/project.xcworkspace
+/xcshareddata/
+/swiftlint_report/
+/fastlane/report.xml
+/fastlane/Preview.html
+/fastlane/screenshots
+/fastlane/test_output
+/slather-report/
+*.xcodeproj/*
+!*.xcodeproj/project.pbxproj
+!*.xcodeproj/project.xcworkspace/
+!*.xcodeproj/xcshareddata/
+KOSMobile/aarch64-apple-ios
+KOSMobile/aarch64-apple-ios-sim
\ No newline at end of file
diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..e8c57d9
--- /dev/null
+++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/project.pbxproj
@@ -0,0 +1,512 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 6561E0B92C76772200907790 /* KOSMobile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0B02C76772200907790 /* KOSMobile.framework */; };
+ 6561E0BE2C76772200907790 /* KOSMobileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6561E0BD2C76772200907790 /* KOSMobileTests.swift */; };
+ 6561E0BF2C76772200907790 /* KOSMobile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6561E0B32C76772200907790 /* KOSMobile.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 6561E0CB2C76782000907790 /* kos_mobile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6561E0C82C76782000907790 /* kos_mobile.swift */; };
+ 6561E0CC2C76782000907790 /* kos_mobileFFI.h in Headers */ = {isa = PBXBuildFile; fileRef = 6561E0CA2C76782000907790 /* kos_mobileFFI.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 6561E0CF2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */; };
+ 6561E0D12C76785700907790 /* aarch64-apple-ios-libkos_mobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 6561E0BA2C76772200907790 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 6561E0A72C76772200907790 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 6561E0AF2C76772200907790;
+ remoteInfo = KOSMobile;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 6561E0B02C76772200907790 /* KOSMobile.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KOSMobile.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6561E0B32C76772200907790 /* KOSMobile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KOSMobile.h; sourceTree = ""; };
+ 6561E0B82C76772200907790 /* KOSMobileTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KOSMobileTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6561E0BD2C76772200907790 /* KOSMobileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KOSMobileTests.swift; sourceTree = ""; };
+ 6561E0C82C76782000907790 /* kos_mobile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = kos_mobile.swift; path = ../../../binds/kos_mobile.swift; sourceTree = ""; };
+ 6561E0C92C76782000907790 /* kos_mobileFFI.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = kos_mobileFFI.modulemap; path = ../../../binds/kos_mobileFFI.modulemap; sourceTree = ""; };
+ 6561E0CA2C76782000907790 /* kos_mobileFFI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kos_mobileFFI.h; path = ../../../binds/kos_mobileFFI.h; sourceTree = ""; };
+ 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "aarch64-apple-ios-sim-libkos_mobile.a"; path = "../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a"; sourceTree = ""; };
+ 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "aarch64-apple-ios-libkos_mobile.a"; path = "../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 6561E0AD2C76772200907790 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6561E0D12C76785700907790 /* aarch64-apple-ios-libkos_mobile.a in Frameworks */,
+ 6561E0CF2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6561E0B52C76772200907790 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6561E0B92C76772200907790 /* KOSMobile.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 6561E0A62C76772200907790 = {
+ isa = PBXGroup;
+ children = (
+ 6561E0B22C76772200907790 /* KOSMobile */,
+ 6561E0BC2C76772200907790 /* KOSMobileTests */,
+ 6561E0B12C76772200907790 /* Products */,
+ 6561E0CD2C76784A00907790 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 6561E0B12C76772200907790 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 6561E0B02C76772200907790 /* KOSMobile.framework */,
+ 6561E0B82C76772200907790 /* KOSMobileTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 6561E0B22C76772200907790 /* KOSMobile */ = {
+ isa = PBXGroup;
+ children = (
+ 6561E0C82C76782000907790 /* kos_mobile.swift */,
+ 6561E0CA2C76782000907790 /* kos_mobileFFI.h */,
+ 6561E0C92C76782000907790 /* kos_mobileFFI.modulemap */,
+ 6561E0B32C76772200907790 /* KOSMobile.h */,
+ );
+ path = KOSMobile;
+ sourceTree = "";
+ };
+ 6561E0BC2C76772200907790 /* KOSMobileTests */ = {
+ isa = PBXGroup;
+ children = (
+ 6561E0BD2C76772200907790 /* KOSMobileTests.swift */,
+ );
+ path = KOSMobileTests;
+ sourceTree = "";
+ };
+ 6561E0CD2C76784A00907790 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6561E0D02C76785700907790 /* aarch64-apple-ios-libkos_mobile.a */,
+ 6561E0CE2C76784A00907790 /* aarch64-apple-ios-sim-libkos_mobile.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 6561E0AB2C76772200907790 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6561E0CC2C76782000907790 /* kos_mobileFFI.h in Headers */,
+ 6561E0BF2C76772200907790 /* KOSMobile.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 6561E0AF2C76772200907790 /* KOSMobile */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 6561E0C22C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobile" */;
+ buildPhases = (
+ 6561E0AB2C76772200907790 /* Headers */,
+ 6561E0AC2C76772200907790 /* Sources */,
+ 6561E0AD2C76772200907790 /* Frameworks */,
+ 6561E0AE2C76772200907790 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = KOSMobile;
+ productName = KOSMobile;
+ productReference = 6561E0B02C76772200907790 /* KOSMobile.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ 6561E0B72C76772200907790 /* KOSMobileTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 6561E0C52C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobileTests" */;
+ buildPhases = (
+ 6561E0B52C76772200907790 /* Frameworks */,
+ 6561E0B42C76772200907790 /* Sources */,
+ 6561E0B62C76772200907790 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 6561E0BB2C76772200907790 /* PBXTargetDependency */,
+ );
+ name = KOSMobileTests;
+ productName = KOSMobileTests;
+ productReference = 6561E0B82C76772200907790 /* KOSMobileTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 6561E0A72C76772200907790 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1520;
+ LastUpgradeCheck = 1520;
+ TargetAttributes = {
+ 6561E0AF2C76772200907790 = {
+ CreatedOnToolsVersion = 15.2;
+ LastSwiftMigration = 1520;
+ };
+ 6561E0B72C76772200907790 = {
+ CreatedOnToolsVersion = 15.2;
+ };
+ };
+ };
+ buildConfigurationList = 6561E0AA2C76772200907790 /* Build configuration list for PBXProject "KOSMobile" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 6561E0A62C76772200907790;
+ productRefGroup = 6561E0B12C76772200907790 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 6561E0AF2C76772200907790 /* KOSMobile */,
+ 6561E0B72C76772200907790 /* KOSMobileTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 6561E0AE2C76772200907790 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6561E0B62C76772200907790 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 6561E0AC2C76772200907790 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6561E0CB2C76782000907790 /* kos_mobile.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6561E0B42C76772200907790 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6561E0BE2C76772200907790 /* KOSMobileTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 6561E0BB2C76772200907790 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 6561E0AF2C76772200907790 /* KOSMobile */;
+ targetProxy = 6561E0BA2C76772200907790 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 6561E0C02C76772200907790 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Debug;
+ };
+ 6561E0C12C76772200907790 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ CURRENT_PROJECT_VERSION = 1;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.2;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
+ VERSION_INFO_PREFIX = "";
+ };
+ name = Release;
+ };
+ 6561E0C32C76772200907790 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ "OTHER_LDFLAGS[sdk=iphoneos*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a\"";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a\"";
+ PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobile;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 6561E0C42C76772200907790 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEFINES_MODULE = YES;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ DYLIB_INSTALL_NAME_BASE = "@rpath";
+ ENABLE_MODULE_VERIFIER = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
+ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
+ "OTHER_LDFLAGS[sdk=iphoneos*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios/release/aarch64-apple-ios-libkos_mobile.a\"";
+ "OTHER_LDFLAGS[sdk=iphonesimulator*]" = "\"$(SRCROOT)/../../../../../target/aarch64-apple-ios-sim/release/aarch64-apple-ios-sim-libkos_mobile.a\"";
+ PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobile;
+ PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+ SKIP_INSTALL = YES;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+ SUPPORTS_MACCATALYST = NO;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 6561E0C62C76772200907790 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobileTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 6561E0C72C76772200907790 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = io.klever.KOSMobileTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 6561E0AA2C76772200907790 /* Build configuration list for PBXProject "KOSMobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6561E0C02C76772200907790 /* Debug */,
+ 6561E0C12C76772200907790 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 6561E0C22C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobile" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6561E0C32C76772200907790 /* Debug */,
+ 6561E0C42C76772200907790 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 6561E0C52C76772200907790 /* Build configuration list for PBXNativeTarget "KOSMobileTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6561E0C62C76772200907790 /* Debug */,
+ 6561E0C72C76772200907790 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 6561E0A72C76772200907790 /* Project object */;
+}
diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme
new file mode 100644
index 0000000..acac82b
--- /dev/null
+++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile.xcodeproj/xcshareddata/xcschemes/KOSMobile.xcscheme
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h
new file mode 100644
index 0000000..1fd9dd2
--- /dev/null
+++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobile/KOSMobile.h
@@ -0,0 +1,15 @@
+//
+// KOSMobile.h
+// KOSMobile
+//
+// Created by Daniel Falcรฃo on 21/08/24.
+//
+
+#import
+#import
+
+//! Project version number for KOSMobile.
+FOUNDATION_EXPORT double KOSMobileVersionNumber;
+
+//! Project version string for KOSMobile.
+FOUNDATION_EXPORT const unsigned char KOSMobileVersionString[];
\ No newline at end of file
diff --git a/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift
new file mode 100644
index 0000000..fa884a6
--- /dev/null
+++ b/packages/kos-mobile/ios/framework/KOSMobile/KOSMobileTests/KOSMobileTests.swift
@@ -0,0 +1,69 @@
+//
+// KOSMobileTests.swift
+// KOSMobileTests
+//
+// Created by Daniel Falcรฃo on 21/08/24.
+//
+
+import XCTest
+@testable import KOSMobile
+
+final class KOSMobileTests: XCTestCase {
+
+ func testKOS() {
+
+ let dataToEncrypt = "Hello"
+ let password = "password"
+ let klvChainId = Int32(38)
+ let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
+ let klvPk0 = "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d"
+ let klvAddr0 = "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy"
+ let klvPath0 = "m/44'/690'/0'/0'/0'"
+ let klvKey0 = "e41b323a571fd955e09cd41660ff4465c3f44693c87f2faea4a0fc408727c8ea"
+
+ let isValidMnemonicValid = validateMnemonic(mnemonic: mnemonic)
+ XCTAssertTrue(isValidMnemonicValid)
+
+ let isInvalidMnemonicValid = validateMnemonic(mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon klv")
+ XCTAssertFalse(isInvalidMnemonicValid)
+
+ let mnemonic12 = try! generateMnemonic(size: 12)
+ XCTAssertTrue(mnemonic12.split(separator: " ").count == 12)
+
+ let mnemonic24 = try! generateMnemonic(size: 24)
+ XCTAssertTrue(mnemonic24.split(separator: " ").count == 24)
+
+ let gmcEncryptedData = try! encryptWithGmc(data: dataToEncrypt, password: password)
+ XCTAssertTrue(!gmcEncryptedData.isEmpty)
+
+ let cbcEncryptedData = try! encryptWithCbc(data: dataToEncrypt, password: password)
+ XCTAssertTrue(!cbcEncryptedData.isEmpty)
+
+ let cfbEncryptedData = try! encryptWithCfb(data: dataToEncrypt, password: password)
+ XCTAssertTrue(!cfbEncryptedData.isEmpty)
+
+ let gmcDecryptedData = try! decrypt(data: gmcEncryptedData, password: password)
+ XCTAssertEqual(dataToEncrypt, gmcDecryptedData)
+
+ let cbcDecryptedData = try! decrypt(data: cbcEncryptedData, password: password)
+ XCTAssertEqual(dataToEncrypt, cbcDecryptedData)
+
+ let cfbDecryptedData = try! decrypt(data: cfbEncryptedData, password: password)
+ XCTAssertEqual(dataToEncrypt, cfbDecryptedData)
+
+ let walletFromMnemonic = try! generateWalletFromMnemonic(mnemonic: mnemonic, chainId: klvChainId, index: 0, useLegacyPath: false)
+ XCTAssertEqual(klvChainId, walletFromMnemonic.chainId)
+ XCTAssertEqual(klvPk0, walletFromMnemonic.privateKey)
+ XCTAssertEqual(klvAddr0, walletFromMnemonic.address)
+ XCTAssertEqual(klvPath0, walletFromMnemonic.path)
+ XCTAssertEqual(klvKey0, walletFromMnemonic.publicKey)
+
+ let walletFromPk = try! generateWalletFromPrivateKey(chainId: klvChainId, privateKey: klvPk0)
+ XCTAssertEqual(klvChainId, walletFromPk.chainId)
+ XCTAssertEqual(klvPk0, walletFromPk.privateKey)
+ XCTAssertEqual(klvAddr0, walletFromPk.address)
+ XCTAssertEqual("", walletFromPk.path)
+ XCTAssertEqual(klvKey0, walletFromPk.publicKey)
+
+ }
+}
\ No newline at end of file
diff --git a/packages/kos-mobile/src/bin/uniffi-bindgen.rs b/packages/kos-mobile/src/bin/uniffi-bindgen.rs
new file mode 100644
index 0000000..f6cff6c
--- /dev/null
+++ b/packages/kos-mobile/src/bin/uniffi-bindgen.rs
@@ -0,0 +1,3 @@
+fn main() {
+ uniffi::uniffi_bindgen_main()
+}
diff --git a/packages/kos-mobile/src/lib.rs b/packages/kos-mobile/src/lib.rs
new file mode 100644
index 0000000..3ff6631
--- /dev/null
+++ b/packages/kos-mobile/src/lib.rs
@@ -0,0 +1,308 @@
+use hex::FromHexError;
+use hex::ToHex;
+
+use kos_crypto::cipher;
+use kos_crypto::cipher::CipherAlgo;
+use kos_sdk::chain::Chain;
+use kos_sdk::models::PathOptions;
+use kos_sdk::wallet::Wallet;
+use kos_types::error::Error as KosError;
+
+uniffi::setup_scaffolding!();
+
+#[derive(Debug, thiserror::Error, uniffi::Error)]
+enum KOSError {
+ #[error("UnsupportedChainError: Unsupported chain {id}")]
+ UnsupportedChain { id: String },
+ #[error("KOSDelegateError: {0}")]
+ KOSDelegate(String),
+ #[error("HexDecodeError: {0}")]
+ HexDecode(String),
+}
+
+impl From for KOSError {
+ fn from(err: KosError) -> Self {
+ KOSError::KOSDelegate(err.to_string())
+ }
+}
+
+impl From for KOSError {
+ fn from(err: FromHexError) -> Self {
+ KOSError::HexDecode(err.to_string())
+ }
+}
+
+#[derive(uniffi::Record)]
+struct KOSAccount {
+ pub chain_id: i32,
+ pub private_key: String,
+ pub public_key: String,
+ pub address: String,
+ pub path: String,
+}
+
+#[uniffi::export]
+fn generate_mnemonic(size: i32) -> Result {
+ Ok(kos_crypto::mnemonic::generate_mnemonic(size as usize)?.to_phrase())
+}
+
+#[uniffi::export]
+fn validate_mnemonic(mnemonic: String) -> bool {
+ kos_crypto::mnemonic::validate_mnemonic(mnemonic.as_str()).is_ok()
+}
+
+#[uniffi::export]
+fn generate_wallet_from_mnemonic(
+ mnemonic: String,
+ chain_id: i32,
+ index: i32,
+ use_legacy_path: bool,
+) -> Result {
+ let chain = get_chain_by(chain_id)?;
+ let mut path_options = PathOptions::new(index as u32);
+ path_options.set_legacy(use_legacy_path);
+ let path = chain.get_path(&path_options)?;
+ let wallet = Wallet::from_mnemonic(chain, mnemonic, path, None)?;
+ Ok(KOSAccount {
+ chain_id,
+ private_key: wallet.get_private_key(),
+ public_key: wallet.get_public_key(),
+ address: wallet.get_address(),
+ path: wallet.get_path(),
+ })
+}
+
+#[uniffi::export]
+fn generate_wallet_from_private_key(
+ chain_id: i32,
+ private_key: String,
+) -> Result {
+ let chain = get_chain_by(chain_id)?;
+ let wallet = Wallet::from_private_key(chain, private_key)?;
+ Ok(KOSAccount {
+ chain_id,
+ private_key: wallet.get_private_key(),
+ public_key: wallet.get_public_key(),
+ address: wallet.get_address(),
+ path: wallet.get_path(),
+ })
+}
+
+#[uniffi::export]
+fn encrypt_with_gmc(data: String, password: String) -> Result {
+ let encrypted_data = CipherAlgo::GMC.encrypt(data.as_bytes(), password.as_str())?;
+ Ok(encrypted_data.encode_hex())
+}
+
+#[uniffi::export]
+fn encrypt_with_cbc(data: String, password: String) -> Result {
+ let encrypted_data = CipherAlgo::CBC.encrypt(data.as_bytes(), password.as_str())?;
+ Ok(encrypted_data.encode_hex())
+}
+
+#[uniffi::export]
+fn encrypt_with_cfb(data: String, password: String) -> Result {
+ let encrypted_data = CipherAlgo::CFB.encrypt(data.as_bytes(), password.as_str())?;
+ Ok(encrypted_data.encode_hex())
+}
+
+#[uniffi::export]
+fn decrypt(data: String, password: String) -> Result {
+ let data_in_byte = hex::decode(data)?;
+ let decrypted_data = cipher::decrypt(&data_in_byte, password.as_str())?;
+ Ok(String::from_utf8_lossy(&decrypted_data).to_string())
+}
+
+fn get_chain_by(id: i32) -> Result {
+ let id_u8 = u8::try_from(id).map_err(|_| KOSError::UnsupportedChain { id: id.to_string() })?;
+ let chain = Chain::get_by_code(id_u8)
+ .ok_or_else(|| KOSError::UnsupportedChain { id: id.to_string() })?;
+ Ok(chain)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::*;
+
+ #[test]
+ fn should_generate_mnemonic() {
+ let size = 12;
+ match generate_mnemonic(size) {
+ Ok(mnemonic) => assert!(!mnemonic.is_empty(), "The mnemonic should not be empty"),
+ Err(_) => panic!("unexpected error!"),
+ }
+ }
+
+ #[test]
+ fn should_fail_to_generate_mnemonic() {
+ let size = -1;
+ match generate_mnemonic(size) {
+ Ok(_) => panic!("A error was expected but found a mnemonic"),
+ Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), "Invalid error"),
+ }
+ }
+
+ #[test]
+ fn should_validate_mnemonic_with_success() {
+ let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let result = validate_mnemonic(mnemonic);
+ assert!(result, "The mnemonic should be valid")
+ }
+
+ #[test]
+ fn should_validate_mnemonic_with_failure() {
+ let mnemonic = "abandon xxx abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let result = validate_mnemonic(mnemonic);
+ assert!(!result, "The mnemonic should be not valid")
+ }
+
+ #[test]
+ fn should_fail_to_get_account_from_mnemonic_with_invalid_chain() {
+ let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let index = 0;
+ let chain_id = 999;
+ match generate_wallet_from_mnemonic(mnemonic, chain_id, index, false) {
+ Ok(_) => panic!("A error was expected but found a mnemonic"),
+ Err(e) => {
+ if let KOSError::UnsupportedChain { id } = e {
+ assert_eq!(id, chain_id.to_string(), "Invalid error");
+ } else {
+ panic!("Expected UnsupportedChainError but found different error");
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn should_get_account_from_mnemonic() {
+ let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let index = 0;
+ let chain_id = 38;
+ match generate_wallet_from_mnemonic(mnemonic, chain_id, index, false) {
+ Ok(account) => {
+ assert_eq!(
+ account.address,
+ "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(),
+ "The address doesn't match"
+ );
+ assert_eq!(
+ account.private_key,
+ "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string(),
+ "The private_key doesn't match"
+ );
+ assert_eq!(account.chain_id, chain_id, "The chain_id doesn't match");
+ }
+ Err(_) => panic!("unexpected error!"),
+ }
+ }
+
+ #[test]
+ fn should_get_all_supported_chains_account_from_mnemonic() {
+ let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let index = 0;
+ for (&chain_code, _) in Chain::get_chains().iter() {
+ println!("code = {}", chain_code)
+ }
+ for (&chain_code, _) in Chain::get_chains().iter() {
+ match generate_wallet_from_mnemonic(
+ mnemonic.clone(),
+ i32::from(chain_code),
+ index,
+ false,
+ ) {
+ Ok(account) => {
+ assert!(
+ !account.address.is_empty(),
+ "The address for chain {} is empty",
+ chain_code
+ );
+ assert!(
+ !account.private_key.is_empty(),
+ "The private_key for chain {} is empty",
+ chain_code
+ );
+ assert_eq!(
+ account.chain_id,
+ i32::from(chain_code),
+ "The chain_id doesn't match"
+ );
+ }
+ Err(e) => panic!("unexpected error! {}", e.to_string()),
+ }
+ }
+ }
+
+ #[test]
+ fn should_get_account_from_private_key() {
+ let private_key =
+ "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string();
+ let chain_id = 38;
+ match generate_wallet_from_private_key(chain_id, private_key) {
+ Ok(account) => {
+ assert_eq!(
+ account.address,
+ "klv1usdnywjhrlv4tcyu6stxpl6yvhplg35nepljlt4y5r7yppe8er4qujlazy".to_string(),
+ "The address doesn't match"
+ );
+ assert_eq!(
+ account.private_key,
+ "8734062c1158f26a3ca8a4a0da87b527a7c168653f7f4c77045e5cf571497d9d".to_string(),
+ "The private_key doesn't match"
+ );
+ assert_eq!(account.chain_id, chain_id, "The chain_id doesn't match");
+ }
+ Err(_) => panic!("unexpected error!"),
+ }
+ }
+
+ #[test]
+ fn should_fail_to_get_account_from_private_key() {
+ let private_key = "".to_string();
+ let chain_id = 38;
+ match generate_wallet_from_private_key(chain_id, private_key) {
+ Ok(account) => panic!(
+ "A error was expected but found a pk {}.",
+ account.private_key
+ ),
+ Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), " Invalid error"),
+ }
+ }
+
+ #[test]
+ fn should_encrypt_with_gmc_and_decrypt_data() {
+ let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let password = "myPass".to_string();
+ let encrypted_data = encrypt_with_gmc(original_data.clone(), password.clone()).unwrap();
+ let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap();
+ assert_eq!(original_data, decrypted_data, "The data is not the same");
+ }
+
+ #[test]
+ fn should_encrypt_with_cbc_and_decrypt_data() {
+ let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let password = "myPass".to_string();
+ let encrypted_data = encrypt_with_cbc(original_data.clone(), password.clone()).unwrap();
+ let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap();
+ assert_eq!(original_data, decrypted_data, "The data is not the same");
+ }
+
+ #[test]
+ fn should_encrypt_with_cbf_and_decrypt_data() {
+ let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let password = "myPass".to_string();
+ let encrypted_data = encrypt_with_cfb(original_data.clone(), password.clone()).unwrap();
+ let decrypted_data = decrypt(encrypted_data, password.clone()).unwrap();
+ assert_eq!(original_data, decrypted_data, "The data is not the same");
+ }
+
+ #[test]
+ fn should_fail_to_decrypt_with_wrong_password() {
+ let original_data = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about".to_string();
+ let password = "myPass".to_string();
+ let encrypted_data = encrypt_with_gmc(original_data.clone(), password.clone()).unwrap();
+ match decrypt(encrypted_data, "wrong".to_string()) {
+ Ok(_) => panic!("A error was expected but found a decrypted data"),
+ Err(e) => assert!(matches!(e, KOSError::KOSDelegate(..)), " Invalid error"),
+ }
+ }
+}
diff --git a/packages/kos-sdk/Cargo.toml b/packages/kos-sdk/Cargo.toml
index 30f96f6..32004f1 100644
--- a/packages/kos-sdk/Cargo.toml
+++ b/packages/kos-sdk/Cargo.toml
@@ -40,6 +40,8 @@ rlp = "0.5"
reqwest = { workspace = true, default-features = false, features = ["rustls-tls", "blocking", "json"] }
wasm-bindgen-futures = "0.4"
+lazy_static = { workspace = true }
+
kos-types = { workspace = true, features = ["serde"] }
kos-crypto = { workspace = true }
kos-proto = { workspace = true }
diff --git a/packages/kos-sdk/src/chain.rs b/packages/kos-sdk/src/chain.rs
index bb281a1..1a7087b 100644
--- a/packages/kos-sdk/src/chain.rs
+++ b/packages/kos-sdk/src/chain.rs
@@ -4,7 +4,9 @@ use kos_crypto::keypair::KeyPair;
use kos_types::error::Error;
use kos_types::number::BigNumber;
+use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
use strum::{EnumCount, IntoStaticStr};
use wasm_bindgen::prelude::*;
@@ -17,6 +19,16 @@ macro_rules! createChains {
$($name,)*
}
+ lazy_static! {
+ static ref CHAIN_MAP: HashMap = {
+ let mut map = HashMap::new();
+ $(map.insert($name::base_chain().chain_code, Chain::$name);)*
+ // remove the NONE chain
+ map.remove(&0);
+ map
+ };
+ }
+
impl Chain {
pub fn base_chain(&self) -> BaseChain {
match self {
@@ -54,6 +66,14 @@ macro_rules! createChains {
}
}
+ pub fn get_by_code(code: u8) -> Option {
+ CHAIN_MAP.get(&code).cloned()
+ }
+
+ pub fn get_chains() -> HashMap {
+ CHAIN_MAP.clone()
+ }
+
/// Sign digest data with the private key.
pub fn sign_digest(&self, digest: &[u8], keypair: &KeyPair) -> Result, Error> {
match self {