Repository: bitcoindevkit/bdk-ffi (language: python)

Issue:
Add change descriptor to Wallet

Use snake_case for identifier



Discussion / hints:

Fix patch:
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 4f1c5cd8..00000000
--- a/.editorconfig
+++ /dev/null
@@ -1,29 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-end_of_line = lf
-indent_size = 2
-indent_style = space
-trim_trailing_whitespace = true
-insert_final_newline = true
-
-[*.md]
-trim_trailing_whitespace = false
-
-[*.rs]
-indent_size = 4
-
-[*.kt]
-indent_size = 4
-
-[*.gradle]
-indent_size = 4
-
-[tests/**/*.rs]
-charset = utf-8
-end_of_line = unset
-indent_size = unset
-indent_style = unset
-trim_trailing_whitespace = unset
-insert_final_newline = unset
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 42afd7e3..947a0928 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,4 +1,4 @@
-<!-- You can erase any parts of this template not applicable to your Pull Request. -->
+<!-- Erase any parts of this template not applicable to your Pull Request. -->
 
 ### Description
 
diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
index 7a3cb072..b170630c 100644
--- a/.github/workflows/audit.yml
+++ b/.github/workflows/audit.yml
@@ -9,12 +9,9 @@ on:
     - cron: '0 0 * * 0' # Once per week
 
 jobs:
-
   security_audit:
+    name: Security Audit
     runs-on: ubuntu-20.04
-    defaults:
-      run:
-        working-directory: bdk-ffi
     steps:
       - uses: actions/checkout@v2
       - uses: actions-rs/audit-check@v1
diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml
index 70d390d3..af447591 100644
--- a/.github/workflows/cont_integration.yml
+++ b/.github/workflows/cont_integration.yml
@@ -12,9 +12,6 @@ jobs:
   build-test:
     name: Build and test
     runs-on: ubuntu-latest
-    defaults:
-      run:
-        working-directory: bdk-ffi
     strategy:
       matrix:
         rust:
@@ -54,9 +51,6 @@ jobs:
   fmt:
     name: Rust fmt
     runs-on: ubuntu-latest
-    defaults:
-      run:
-        working-directory: bdk-ffi
     steps:
       - name: Checkout
         uses: actions/checkout@v2
diff --git a/.github/workflows/publish-android.yaml b/.github/workflows/publish-android.yaml
new file mode 100644
index 00000000..ebe1f712
--- /dev/null
+++ b/.github/workflows/publish-android.yaml
@@ -0,0 +1,55 @@
+name: Publish bdk-android to Maven Central
+on: [workflow_dispatch]
+
+env:
+  ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
+  # By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
+  # ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
+
+jobs:
+  build:
+    runs-on: ubuntu-20.04
+    steps:
+      - name: Install Android NDK 21.4.7075529
+        run: |
+          ANDROID_ROOT=/usr/local/lib/android
+          ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
+          SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
+          echo "y" | $SDKMANAGER "ndk;21.4.7075529"
+
+      - name: Check out PR branch
+        uses: actions/checkout@v2
+
+      - name: cache
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            ./target
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: temurin
+          java-version: 11
+
+      - name: Install rust android targets
+        run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
+
+      - name: Build bdk-android library
+        run: |
+          cd bdk-android
+          ./gradlew buildAndroidLib
+
+      - name: Publish to Maven Local and Maven Central
+        env:
+          ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }}
+          ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }}
+          ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }}
+        run: |
+          cd bdk-android
+          ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
diff --git a/.github/workflows/publish-jvm.yaml b/.github/workflows/publish-jvm.yaml
new file mode 100644
index 00000000..74daff63
--- /dev/null
+++ b/.github/workflows/publish-jvm.yaml
@@ -0,0 +1,93 @@
+name: Publish bdk-jvm to Maven Central
+on: [workflow_dispatch]
+
+jobs:
+  build-jvm-macOS-M1-native-lib:
+    name: Create M1 and x86_64 JVM native binaries
+    runs-on: macos-12
+    steps:
+      - name: Checkout publishing branch
+        uses: actions/checkout@v2
+
+      - name: Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            ./target
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: temurin
+          java-version: 11
+
+      - name: Install aarch64 Rust target
+        run: rustup target add aarch64-apple-darwin
+
+      - name: Build bdk-jvm library
+        run: |
+          cd bdk-jvm
+          ./gradlew buildJvmLib
+
+      # build aarch64 + x86_64 native libraries and upload
+      - name: Upload macOS native libraries for reuse in publishing job
+        uses: actions/upload-artifact@v3
+        with:
+          # name: no name is required because we upload the entire directory
+          # the default name "artifact" will be used
+          path: /Users/runner/work/bdk-kotlin/bdk-kotlin/bdk-jvm/lib/src/main/resources/
+
+  build-jvm-full-library:
+    name: Create full bdk-jvm library
+    needs: [build-jvm-macOS-M1-native-lib]
+    runs-on: ubuntu-22.04
+    steps:
+      - name: Checkout publishing branch
+        uses: actions/checkout@v2
+
+      - name: Update bdk-ffi git submodule
+        run: |
+          git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git
+          git submodule update --init bdk-ffi
+
+      - name: Cache
+        uses: actions/cache@v3
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            ./target
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: temurin
+          java-version: 11
+
+      - name: Build bdk-jvm library
+        run: |
+          cd bdk-jvm
+          ./gradlew buildJvmLib
+
+      - name: Download macOS native libraries from previous job
+        uses: actions/download-artifact@v3
+        id: download
+        with:
+          # download the artifact created in the prior job (named "artifact")
+          name: artifact
+          path: ./bdk-jvm/lib/src/main/resources/
+
+      - name: Publish to Maven Central
+        env:
+          ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }}
+          ORG_GRADLE_PROJECT_ossrhUsername: ${{ secrets.NEXUS_USERNAME }}
+          ORG_GRADLE_PROJECT_ossrhPassword: ${{ secrets.NEXUS_PASSWORD }}
+        run: |
+          cd bdk-jvm
+          ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
diff --git a/.gitignore b/.gitignore
index c91c338a..207fd978 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,5 @@
 target
 build
-Cargo.lock
-/bindings/bdk-kotlin/local.properties
 .gradle
 wallet_db
 bdk_ffi_test
@@ -15,3 +13,5 @@ xcuserdata
 .lsp
 .clj-kondo
 .idea/
+.editorconfig
+*/bdk.kt
diff --git a/bdk-ffi/CHANGELOG.md b/CHANGELOG.md
similarity index 100%
rename from bdk-ffi/CHANGELOG.md
rename to CHANGELOG.md
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 00000000..1b00eb38
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1475 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+
+[[package]]
+name = "askama"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
+dependencies = [
+ "askama_derive",
+ "askama_escape",
+ "askama_shared",
+]
+
+[[package]]
+name = "askama_derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
+dependencies = [
+ "askama_shared",
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "askama_escape"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
+
+[[package]]
+name = "askama_shared"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
+dependencies = [
+ "askama_escape",
+ "mime",
+ "mime_guess",
+ "nom",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+ "toml",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "bdk"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c50e3f08fbcd7eb34f11e066a1353986d583f1f87af70f7cd50bff9fce97c817"
+dependencies = [
+ "ahash",
+ "async-trait",
+ "bdk-macros",
+ "bip39",
+ "bitcoin",
+ "electrum-client",
+ "esplora-client",
+ "getrandom",
+ "js-sys",
+ "log",
+ "miniscript",
+ "rand",
+ "rusqlite",
+ "serde",
+ "serde_json",
+ "sled",
+ "tokio",
+]
+
+[[package]]
+name = "bdk-ffi"
+version = "0.11.0"
+dependencies = [
+ "bdk",
+ "uniffi",
+ "uniffi_build",
+ "uniffi_macros",
+]
+
+[[package]]
+name = "bdk-ffi-bindgen"
+version = "0.2.0"
+dependencies = [
+ "anyhow",
+ "camino",
+ "structopt",
+ "uniffi_bindgen",
+]
+
+[[package]]
+name = "bdk-macros"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81c1980e50ae23bb6efa9283ae8679d6ea2c6fa6a99fe62533f65f4a25a1a56c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[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 = "bip39"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f"
+dependencies = [
+ "bitcoin_hashes 0.9.7",
+ "rand_core 0.4.2",
+ "serde",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "bitcoin"
+version = "0.29.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3"
+dependencies = [
+ "base64",
+ "bech32",
+ "bitcoin_hashes 0.11.0",
+ "secp256k1",
+ "serde",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1"
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "camino"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chunked_transfer"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
+
+[[package]]
+name = "clap"
+version = "2.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+dependencies = [
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim 0.8.0",
+ "textwrap 0.11.0",
+ "unicode-width",
+ "vec_map",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
+ "strsim 0.10.0",
+ "termcolor",
+ "textwrap 0.16.0",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck 0.4.0",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "electrum-client"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82a232d46710f8064b7bb2029d82819fc1f5fba053687e0e3bb47cc762af24f6"
+dependencies = [
+ "bitcoin",
+ "byteorder",
+ "libc",
+ "log",
+ "rustls 0.20.7",
+ "serde",
+ "serde_json",
+ "webpki 0.22.0",
+ "webpki-roots 0.22.5",
+ "winapi",
+]
+
+[[package]]
+name = "esplora-client"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b94400573de17e346ab91ec08ad13d37e871e400b9930d4cb2b6b02253e760e"
+dependencies = [
+ "bitcoin",
+ "log",
+ "serde",
+ "ureq",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs-err"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541"
+
+[[package]]
+name = "fs2"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "goblin"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143"
+dependencies = [
+ "log",
+ "plain",
+ "scroll",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashlink"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
+dependencies = [
+ "hashbrown 0.11.2",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "idna"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094"
+dependencies = [
+ "matches",
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
+
+[[package]]
+name = "maybe-uninit"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+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 = "miniscript"
+version = "8.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4975078076f0b7b914a3044ad7432d2a7fcec38edb855afdc672e24ca35b69"
+dependencies = [
+ "bitcoin",
+ "serde",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec 1.10.0",
+ "winapi",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
+
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rusqlite"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "memchr",
+ "smallvec 1.10.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
+dependencies = [
+ "base64",
+ "log",
+ "ring",
+ "sct 0.6.1",
+ "webpki 0.21.4",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
+dependencies = [
+ "log",
+ "ring",
+ "sct 0.7.0",
+ "webpki 0.22.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "scroll"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
+dependencies = [
+ "scroll_derive",
+]
+
+[[package]]
+name = "scroll_derive"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sct"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "sct"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "secp256k1"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff55dc09d460954e9ef2fa8a7ced735a964be9981fd50e870b2b3b0705e14964"
+dependencies = [
+ "bitcoin_hashes 0.11.0",
+ "rand",
+ "secp256k1-sys",
+ "serde",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sled"
+version = "0.34.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
+dependencies = [
+ "crc32fast",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "fs2",
+ "fxhash",
+ "libc",
+ "log",
+ "parking_lot",
+]
+
+[[package]]
+name = "smallvec"
+version = "0.6.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
+dependencies = [
+ "maybe-uninit",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+
+[[package]]
+name = "socks"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
+dependencies = [
+ "byteorder",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "structopt"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
+dependencies = [
+ "clap 2.34.0",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
+dependencies = [
+ "heck 0.3.3",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+
+[[package]]
+name = "tokio"
+version = "1.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+dependencies = [
+ "autocfg",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09c8070a9942f5e7cfccd93f490fdebd230ee3c3c9f107cb25bad5351ef671cf"
+dependencies = [
+ "smallvec 0.6.14",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "uniffi"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f54af5ada67d1173457a99a7bb44a7917f63e7466764cb4714865c7a6678b830"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "camino",
+ "cargo_metadata",
+ "log",
+ "once_cell",
+ "paste",
+ "static_assertions",
+ "uniffi_bindgen",
+ "uniffi_macros",
+]
+
+[[package]]
+name = "uniffi_bindgen"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12cc4af3c0180c7e86c4a3acf2b6587af04ba2567b1e948993df10f421796621"
+dependencies = [
+ "anyhow",
+ "askama",
+ "bincode",
+ "camino",
+ "clap 3.2.23",
+ "fs-err",
+ "goblin",
+ "heck 0.4.0",
+ "once_cell",
+ "paste",
+ "serde",
+ "serde_json",
+ "toml",
+ "uniffi_meta",
+ "weedle2",
+]
+
+[[package]]
+name = "uniffi_build"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "510287c368a9386eb731ebe824a6fc6c82a105e20d020af1aa20519c1c1561db"
+dependencies = [
+ "anyhow",
+ "camino",
+ "uniffi_bindgen",
+]
+
+[[package]]
+name = "uniffi_macros"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c8604503caa2cbcf271578dc51ca236d40e3b22e1514ffa2e638e2c39f6ad10"
+dependencies = [
+ "bincode",
+ "camino",
+ "fs-err",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn",
+ "toml",
+ "uniffi_build",
+ "uniffi_meta",
+]
+
+[[package]]
+name = "uniffi_meta"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd9417cc653937681436b93838d8c5f97a5b8c58697813982ee8810bd1dc3b57"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "ureq"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3131cd6cb18488da91da1d10ed31e966f453c06b65bf010d35638456976a3fd7"
+dependencies = [
+ "base64",
+ "chunked_transfer",
+ "log",
+ "once_cell",
+ "rustls 0.19.1",
+ "serde",
+ "serde_json",
+ "socks",
+ "url",
+ "webpki 0.21.4",
+ "webpki-roots 0.21.1",
+]
+
+[[package]]
+name = "url"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
+dependencies = [
+ "webpki 0.21.4",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.22.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
+dependencies = [
+ "webpki 0.22.0",
+]
+
+[[package]]
+name = "weedle2"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e79c5206e1f43a2306fd64bdb95025ee4228960f2e6c5a8b173f3caaf807741"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 00000000..b8d84b07
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[workspace]
+members = ["bdk-ffi", "bdk-ffi-bindgen"]
+default-members = ["bdk-ffi", "bdk-ffi-bindgen"]
+exclude = ["api-docs", "bdk-android", "bdk-jvm", "bdk-python", "bdk-swift"]
+
+[profile.release-smaller]
+inherits = "release"
+opt-level = 'z'     # Optimize for size.
+lto = true          # Enable Link Time Optimization
+codegen-units = 1   # Reduce number of codegen units to increase optimizations.
+panic = 'abort'     # Abort on panic
+strip = true        # Strip symbols from binary*
\ No newline at end of file
diff --git a/bdk-ffi/DEVELOPMENT_CYCLE.md b/DEVELOPMENT_CYCLE.md
similarity index 100%
rename from bdk-ffi/DEVELOPMENT_CYCLE.md
rename to DEVELOPMENT_CYCLE.md
diff --git a/bdk-ffi/LICENSE b/LICENSE
similarity index 100%
rename from bdk-ffi/LICENSE
rename to LICENSE
diff --git a/bdk-ffi/LICENSE-APACHE b/LICENSE-APACHE
similarity index 100%
rename from bdk-ffi/LICENSE-APACHE
rename to LICENSE-APACHE
diff --git a/bdk-ffi/LICENSE-MIT b/LICENSE-MIT
similarity index 100%
rename from bdk-ffi/LICENSE-MIT
rename to LICENSE-MIT
diff --git a/PGP-BDK-BINDINGS.asc b/PGP-BDK-BINDINGS.asc
new file mode 100644
index 00000000..6b5af636
--- /dev/null
+++ b/PGP-BDK-BINDINGS.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEYw6xkRYJKwYBBAHaRw8BAQdAg+VLXuidDqeP015H/QMlESJyQeIntTUoQkbk
++IFu+jO0M2JpdGNvaW5kZXZraXQtYmluZGluZ3MgPGJpbmRpbmdzQGJpdGNvaW5k
+ZXZraXQub3JnPoiTBBMWCgA7FiEEiK2TrEWJ/QkP87jRJ2jEPogDxqMFAmMOsZEC
+GwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQJ2jEPogDxqPQTgEA292D
+RQaxDTJ4k91D0w50Vrd0NSNUwlsERz9XJ64abWABAP99vGMmq2pfrngTQqjLgLe8
+0YhQ+VML2x/B0LSN6MgNuDgEYw6xkRIKKwYBBAGXVQEFAQEHQEkUJv+/Wzx7nNiX
+eti3HkeT6ZNAuCExPE4F7jxHNQ1TAwEIB4h4BBgWCgAgFiEEiK2TrEWJ/QkP87jR
+J2jEPogDxqMFAmMOsZECGwwACgkQJ2jEPogDxqObPQEA/B0xNew03KM0JP630efG
+QT/3Caq/jx86pLwnB7XqWI8BAOKmqrOEiwCBjhaIpzC3/1M+aZuPRUL3V91uPxpM
+jFAJ
+=vvmK
+-----END PGP PUBLIC KEY BLOCK-----
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..251cf67e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,245 @@
+# Native language bindings for BDK
+
+<p>
+    <a href="https://github.com/bitcoindevkit/bdk-ffi/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
+    <a href="https://github.com/bitcoindevkit/bdk-ffi/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk-ffi/workflows/CI/badge.svg"></a>
+    <a href="https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html"><img alt="Rustc Version 1.61.0+" src="https://img.shields.io/badge/rustc-1.61.0%2B-lightgrey.svg"/></a>
+    <a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
+  </p>
+
+The workspace in this repository creates the `libbdkffi` multi-language library for the rust based 
+[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for 
+generating the actual language binding code used to access the `libbdkffi` library.
+
+Each supported language has its own repository that includes this project as a [git submodule]. 
+The rust code in this project is a wrapper around the [bdk] library to expose it's APIs in a 
+uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
+
+## Supported target languages and platforms
+
+The below repositories include instructions for using, building, and publishing the native 
+language binding for [bdk] supported by this project.
+
+| Language | Platform     | Repository   |
+| -------- | ------------ | ------------ |
+| Kotlin   | jvm          | [bdk-kotlin] |
+| Kotlin   | android      | [bdk-kotlin] |
+| Swift    | iOS, macOS   | [bdk-swift]  |
+| Python   | linux, macOS | [bdk-python] |
+
+## Language bindings generator tool
+
+Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages. 
+To run `bdk-ffi-bindgen` and see the available options use the command:
+```shell
+cargo run -p bdk-ffi-bindgen -- --help
+```
+
+[bdk]: https://github.com/bitcoindevkit/bdk
+[Bitcoin Dev Kit]: https://github.com/bitcoindevkit
+[git submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
+[uniffi-rs]: https://github.com/mozilla/uniffi-rs
+
+[bdk-kotlin]: https://github.com/bitcoindevkit/bdk-kotlin
+[bdk-swift]: https://github.com/bitcoindevkit/bdk-swift
+[bdk-python]: https://github.com/bitcoindevkit/bdk-python
+
+## Contributing
+
+### Adding new structs and functions
+
+See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
+
+#### For pass by value objects
+
+1. create new rust struct with only fields that are supported UniFFI types
+1. update mapping `bdk.udl` file with new `dictionary`
+
+#### For pass by reference values 
+
+1. create wrapper rust struct/impl with only fields that are `Sync + Send`
+1. update mapping `bdk.udl` file with new `interface`
+
+## Goals
+
+1. Language bindings should feel idiomatic in target languages/platforms
+1. Adding new targets should be easy
+1. Getting up and running should be easy
+1. Contributing should be easy
+1. Get it right, then automate
+
+# bdk-kotlin
+
+This project builds .jar and .aar packages for the `jvm` and `android` platforms that provide
+[Kotlin] language bindings for the [`bdk`] library. The Kotlin language bindings are created by the
+[`bdk-ffi`] project which is included as a git submodule of this repository.
+
+## How to Use
+
+To use the Kotlin language bindings for [`bdk`] in your `jvm` or `android` project add the
+following to your gradle dependencies:
+```groovy
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+  
+  // for jvm
+  implementation 'org.bitcoindevkit:bdk-jvm:<version>'
+  // OR for android
+  implementation 'org.bitcoindevkit:bdk-android:<version>'
+  
+}
+```
+
+You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
+
+```kotlin
+import org.bitcoindevkit.*
+
+// ...
+
+val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
+val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
+
+val databaseConfig = DatabaseConfig.Memory
+
+val blockchainConfig =
+  BlockchainConfig.Electrum(
+    ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u)
+  )
+val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
+val newAddress = wallet.getNewAddress()
+```
+
+### Example Projects
+
+#### `bdk-android`
+* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
+* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
+
+#### `bdk-jvm`
+* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
+
+### How to build
+_Note that Kotlin version `1.6.10` or later is required to build the library._
+
+1. Clone this repository and initialize and update its [`bdk-ffi`] submodule.
+```shell
+git clone https://github.com/bitcoindevkit/bdk-kotlin
+git submodule update --init
+```
+2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
+3. If building on MacOS install required intel and m1 jvm targets
+```sh
+rustup target add x86_64-apple-darwin aarch64-apple-darwin
+```
+4. Install required targets
+ ```sh
+ rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
+ ```
+5. Install Android SDK and Build-Tools for API level 30+
+6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the
+   build tool), for example (NDK major version 21 is required):
+ ```shell
+ export ANDROID_SDK_ROOT=~/Android/Sdk
+ export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.<NDK_VERSION>
+ ```
+7. Build kotlin bindings
+ ```sh
+ # build JVM library
+ cd bdk-jvm
+ ./gradlew buildJvmLib
+ 
+ # build Android library
+ cd bdk-android
+ ./gradlew buildAndroidLib
+ ```
+8. Start android emulator (must be x86_64) and run tests
+```sh
+./gradlew connectedAndroidTest 
+```
+
+## How to publish
+
+### Publish to your local maven repo
+```shell
+# bdk-jvm
+cd bdk-jvm
+./gradlew publishToMavenLocal --exclude-task signMavenPublication
+
+# bdk-android
+cd bdk-android
+./gradlew publishToMavenLocal --exclude-task signMavenPublication
+```
+
+Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
+```properties
+signing.gnupg.keyName=<YOUR_GNUPG_ID>
+signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
+```
+
+and use the `publishToMavenLocal` task without excluding the signing task:
+```shell
+./gradlew publishToMavenLocal
+```
+
+## Verifying Signatures
+Both libraries and all their corresponding artifacts are signed with a PGP key you can find in the
+root of this repository. To verify the signatures follow the below steps:
+
+1. Import the PGP key in your keyring.
+```shell
+# Navigate to the root of the repository and import the ./PGP-BDK-BINDINGS.asc public key
+gpg --import ./PGP-BDK-BINDINGS.asc
+    
+# Alternatively, you can import the key directly from a public key server
+gpg --keyserver keyserver.ubuntu.com --receive-key 2768C43E8803C6A3
+    
+# Verify that the correct key was imported
+gpg --list-keys
+# You should see the below output
+pub   ed25519 2022-08-31 [SC]
+    88AD93AC4589FD090FF3B8D12768C43E8803C6A3
+uid           [ unknown] bitcoindevkit-bindings <bindings@bitcoindevkit.org>
+sub   cv25519 2022-08-31 [E]
+```
+
+2. Download the binary artifacts and corresponding signature files.
+- from [bdk-jvm]
+    - `bdk-jvm-<version>.jar`
+    - `bdk-jvm-<version>.jar.asc`
+- from [bdk-android]
+    - `bdk-android-<version>.aar`
+    - `bdk-android-<version>.aar.asc`
+
+3. Verify the signatures.
+```shell
+gpg --verify bdk-jvm-<version>.jar.asc 
+gpg --verify bdk-android-<version>.aar.asc
+
+# you should see a "Good signature" result
+gpg: Good signature from "bitcoindevkit-bindings <bindings@bitcoindevkit.org>" [unknown]
+```
+
+### PGP Metadata
+Full key ID: `88AD 93AC 4589 FD09 0FF3 B8D1 2768 C43E 8803 C6A3`  
+Fingerprint: `2768C43E8803C6A3`  
+Name: `bitcoindevkit-bindings`  
+Email: `bindings@bitcoindevkit.org`
+
+[Kotlin]: https://kotlinlang.org/
+[Android Studio]: https://developer.android.com/studio/
+[`bdk`]: https://github.com/bitcoindevkit/bdk
+[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
+["Getting Started (Developer)"]: https://github.com/bitcoindevkit/bdk-ffi#getting-started-developer
+[Gradle Nexus Publish Plugin]: https://github.com/gradle-nexus/publish-plugin
+[bdk-jvm]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-jvm/0.9.0/jar
+[bdk-android]: https://search.maven.org/artifact/org.bitcoindevkit/bdk-android/0.9.0/aar
+
+## Thanks
+
+This project is made possible thanks to the wonderful work by the [mozilla/uniffi-rs] team.
+
+[mozilla/uniffi-rs]: https://github.com/mozilla/uniffi-rs
diff --git a/api-docs/kotlin/Module1.md b/api-docs/kotlin/Module1.md
new file mode 100644
index 00000000..d21953b6
--- /dev/null
+++ b/api-docs/kotlin/Module1.md
@@ -0,0 +1,4 @@
+# Module bdk-android
+The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android.
+
+# Package org.bitcoindevkit
diff --git a/api-docs/kotlin/Module2.md b/api-docs/kotlin/Module2.md
new file mode 100644
index 00000000..8a6eef7f
--- /dev/null
+++ b/api-docs/kotlin/Module2.md
@@ -0,0 +1,4 @@
+# Module bdk-jvm
+The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM.
+
+# Package org.bitcoindevkit
diff --git a/api-docs/kotlin/build.gradle.kts b/api-docs/kotlin/build.gradle.kts
new file mode 100644
index 00000000..b969511c
--- /dev/null
+++ b/api-docs/kotlin/build.gradle.kts
@@ -0,0 +1,46 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+    kotlin("jvm") version "1.7.10"
+
+    // API docs
+    id("org.jetbrains.dokka") version "1.7.10"
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    testImplementation(kotlin("test"))
+}
+
+tasks.test {
+    useJUnitPlatform()
+}
+
+tasks.withType<KotlinCompile> {
+    kotlinOptions.jvmTarget = "1.8"
+}
+
+// tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
+//     dokkaSourceSets {
+//         named("main") {
+//             moduleName.set("bdk-android")
+//             moduleVersion.set("0.11.0")
+//             includes.from("Module1.md")
+//             samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
+//         }
+//     }
+// }
+
+tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
+    dokkaSourceSets {
+        named("main") {
+            moduleName.set("bdk-jvm")
+            moduleVersion.set("0.11.0")
+            includes.from("Module2.md")
+            samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
+        }
+    }
+}
diff --git a/api-docs/kotlin/deploy.sh b/api-docs/kotlin/deploy.sh
new file mode 100644
index 00000000..dd6e442e
--- /dev/null
+++ b/api-docs/kotlin/deploy.sh
@@ -0,0 +1,8 @@
+./gradlew dokkaHtml
+cd build/dokka/html
+git init .
+git add .
+git switch --create gh-pages
+git commit -m "Deploy"
+git remote add origin git@github.com:bitcoindevkit/bdk-kotlin.git
+git push --set-upstream origin gh-pages --force
diff --git a/api-docs/kotlin/gradle.properties b/api-docs/kotlin/gradle.properties
new file mode 100644
index 00000000..7fc6f1ff
--- /dev/null
+++ b/api-docs/kotlin/gradle.properties
@@ -0,0 +1,1 @@
+kotlin.code.style=official
diff --git a/api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar b/api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
Binary files /dev/null and b/api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties b/api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..60c76b34
--- /dev/null
+++ b/api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/api-docs/kotlin/gradlew b/api-docs/kotlin/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/api-docs/kotlin/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${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 "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# 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  ;; #(
+  MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/api-docs/kotlin/gradlew.bat b/api-docs/kotlin/gradlew.bat
new file mode 100644
index 00000000..107acd32
--- /dev/null
+++ b/api-docs/kotlin/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/api-docs/kotlin/settings.gradle.kts b/api-docs/kotlin/settings.gradle.kts
new file mode 100644
index 00000000..3b7cdb5e
--- /dev/null
+++ b/api-docs/kotlin/settings.gradle.kts
@@ -0,0 +1,1 @@
+rootProject.name = "BDK Android and BDK JVM API Docs"
diff --git a/api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt b/api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
new file mode 100644
index 00000000..6da4d187
--- /dev/null
+++ b/api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
@@ -0,0 +1,582 @@
+package org.bitcoindevkit
+
+/**
+ * The cryptocurrency to act on.
+ *
+ * @sample org.bitcoindevkit.networkSample
+ */
+enum class Network {
+    /** Bitcoin's mainnet. */
+    BITCOIN,
+
+    /** Bitcoin’s testnet. */
+    TESTNET,
+
+    /** Bitcoin’s signet. */
+    SIGNET,
+
+    /** Bitcoin’s regtest. */
+    REGTEST,
+}
+
+/**
+ * A derived address and the index it was found at.
+ *
+ * @property index Child index of this address.
+ * @property address Address.
+ *
+ * @sample org.bitcoindevkit.addressInfoSample
+ */
+data class AddressInfo (
+    var index: UInt,
+    var address: String
+)
+
+/**
+ * The address index selection strategy to use to derive an address from the wallet’s external descriptor.
+ *
+ * If you’re unsure which one to use, use `AddressIndex.NEW`.
+ *
+ * @sample org.bitcoindevkit.addressIndexSample
+ */
+enum class AddressIndex {
+    /** Return a new address after incrementing the current descriptor index. */
+    NEW,
+
+    /** Return the address for the current descriptor index if it has not been used in a received transaction.
+     * Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet
+     * has not yet detected an address has been used it could return an already used address.
+     * This function is primarily meant for situations where the caller is untrusted;
+     * for example when deriving donation addresses on-demand for a public web page.
+     */
+    LAST_UNUSED,
+}
+
+/**
+ * Balance differentiated in various categories.
+ *
+ * @property immature All coinbase outputs not yet matured.
+ * @property trustedPending Unconfirmed UTXOs generated by a wallet tx.
+ * @property untrustedPending Unconfirmed UTXOs received from an external wallet.
+ * @property confirmed Confirmed and immediately spendable balance.
+ * @property spendable The sum of trustedPending and confirmed coins.
+ * @property total The whole balance visible to the wallet.
+ *
+ * @sample org.bitcoindevkit.balanceSample
+ */
+data class Balance (
+    var immature: ULong,
+    var trustedPending: ULong,
+    var untrustedPending: ULong,
+    var confirmed: ULong,
+    var spendable: ULong,
+    var total: ULong
+)
+
+/**
+ * Type that can contain any of the database configurations defined by the library.
+ *
+ * @sample org.bitcoindevkit.memoryDatabaseConfigSample
+ * @sample org.bitcoindevkit.sqliteDatabaseConfigSample
+ */
+sealed class DatabaseConfig {
+    /** Configuration for an in-memory database. */
+    object Memory : DatabaseConfig()
+
+    /** Configuration for a Sled database. */
+    data class Sled(val config: SledDbConfiguration) : DatabaseConfig()
+
+    /** Configuration for a SQLite database. */
+    data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig()
+}
+
+/**
+ * Configuration type for a SQLite database.
+ *
+ * @property path Main directory of the DB.
+ */
+data class SqliteDbConfiguration(
+    var path: String,
+)
+
+/**
+ * Configuration type for a SledDB database.
+ *
+ * @property path Main directory of the DB.
+ * @property treeName Name of the database tree, a separated namespace for the data.
+ */
+data class SledDbConfiguration(
+    var path: String,
+    var treeName: String,
+)
+
+/**
+ * Configuration for an Electrum blockchain.
+ *
+ * @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`.
+ * @property socks5 URL of the socks5 proxy server or a Tor service.
+ * @property retry Request retry count.
+ * @property timeout Request timeout (seconds).
+ * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
+ *
+ * @sample org.bitcoindevkit.electrumBlockchainConfigSample
+ */
+data class ElectrumConfig (
+    var url: String,
+    var socks5: String?,
+    var retry: UByte,
+    var timeout: UByte?,
+    var stopGap: ULong
+)
+
+/**
+ * Configuration for an Esplora blockchain.
+ *
+ * @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`.
+ * @property proxy Optional URL of the proxy to use to make requests to the Esplora server.
+ * @property concurrency Number of parallel requests sent to the esplora service (default: 4).
+ * @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
+ * @property timeout Socket timeout.
+ */
+data class EsploraConfig (
+    var baseUrl: String,
+    var proxy: String?,
+    var concurrency: UByte?,
+    var stopGap: ULong,
+    var timeout: ULong?
+)
+
+/**
+ * Type that can contain any of the blockchain configurations defined by the library.
+ *
+ * @sample org.bitcoindevkit.electrumBlockchainConfigSample
+ */
+sealed class BlockchainConfig {
+    /** Electrum client. */
+    data class Electrum(val config: ElectrumConfig) : BlockchainConfig()
+
+    /** Esplora client. */
+    data class Esplora(val config: EsploraConfig) : BlockchainConfig()
+}
+
+/**
+ * A wallet transaction.
+ *
+ * @property fee Fee value (sats) if available. The availability of the fee depends on the backend. It’s never None with an Electrum server backend, but it could be None with a Bitcoin RPC node without txindex that receive funds while offline.
+ * @property received Received value (sats) Sum of owned outputs of this transaction.
+ * @property sent Sent value (sats) Sum of owned inputs of this transaction.
+ * @property txid Transaction id.
+ * @property confirmationTime If the transaction is confirmed, [BlockTime] contains height and timestamp of the block containing the transaction. This property is null for unconfirmed transactions.
+ */
+data class TransactionDetails (
+    var fee: ULong?,
+    var received: ULong,
+    var sent: ULong,
+    var txid: String,
+    var confirmationTime: BlockTime?
+)
+
+/**
+ * A blockchain backend.
+ *
+ * @constructor Create the new blockchain client.
+ *
+ * @param config The blockchain configuration required.
+ *
+ * @sample org.bitcoindevkit.blockchainSample
+ */
+class Blockchain(
+    config: BlockchainConfig
+) {
+    /** Broadcast a transaction. */
+    fun broadcast(psbt: PartiallySignedBitcoinTransaction): String {}
+
+    /** Get the current height of the blockchain. */
+    fun getHeight(): UInt {}
+
+    /** Get the block hash of a given block. */
+    fun getBlockHash(height: UInt): String {}
+}
+
+/**
+ * A partially signed bitcoin transaction.
+ *
+ * @constructor Build a new Partially Signed Bitcoin Transaction.
+ *
+ * @param psbtBase64 The PSBT in base64 format.
+ */
+class PartiallySignedBitcoinTransaction(psbtBase64: String) {
+    /** Return the PSBT in string format, using a base64 encoding. */
+    fun serialize(): String {}
+
+    /** Get the txid of the PSBT. */
+    fun txid(): String {}
+
+    /** Return the transaction as bytes. */
+    fun `extractTx`(): List<UByte>
+
+    /**
+     * Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.
+     * In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
+     */
+    fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction
+}
+
+/**
+ * A reference to a transaction output.
+ *
+ * @property txid The referenced transaction’s txid.
+ * @property vout The index of the referenced output in its transaction’s vout.
+ */
+data class OutPoint (
+    var txid: String,
+    var vout: UInt
+)
+
+/**
+ * A transaction output, which defines new coins to be created from old ones.
+ *
+ * @property value The value of the output, in satoshis.
+ * @property address The address of the output.
+ */
+data class TxOut (
+    var value: ULong,
+    var address: String
+)
+
+/**
+ * An unspent output owned by a [Wallet].
+ *
+ * @property outpoint Reference to a transaction output.
+ * @property txout Transaction output.
+ * @property keychain Type of keychain.
+ * @property isSpent Whether this UTXO is spent or not.
+ */
+data class LocalUtxo (
+    var outpoint: OutPoint,
+    var txout: TxOut,
+    var keychain: KeychainKind,
+    var isSpent: Boolean
+)
+
+/**
+ * Types of keychains.
+ */
+enum class KeychainKind {
+    /** External. */
+    EXTERNAL,
+
+    /** Internal, usually used for change outputs. */
+    INTERNAL,
+}
+
+/**
+ * Block height and timestamp of a block.
+ *
+ * @property height Confirmation block height.
+ * @property timestamp Confirmation block timestamp.
+ */
+data class BlockTime (
+    var height: UInt,
+    var timestamp: ULong,
+)
+
+/**
+ * A Bitcoin wallet.
+ * The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
+ * 1. Output descriptors from which it can derive addresses.
+ * 2. A Database where it tracks transactions and utxos related to the descriptors.
+ * 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
+ *
+ * @constructor Create a BDK wallet.
+ *
+ * @param descriptor The main (or "external") descriptor.
+ * @param changeDescriptor The change (or "internal") descriptor.
+ * @param network The network to act on.
+ * @param databaseConfig The database configuration.
+ *
+ * @sample org.bitcoindevkit.walletSample
+ */
+class Wallet(
+    descriptor: String,
+    changeDescriptor: String,
+    network: Network,
+    databaseConfig: DatabaseConfig,
+) {
+    /**
+     * Return a derived address using the external descriptor, see [AddressIndex] for available address index
+     * selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end
+     * with a * character) then the same address will always be returned for any [AddressIndex].
+     */
+    fun getAddress(addressIndex: AddressIndex): AddressInfo {}
+
+    /** Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
+    fun getBalance(): ULong {}
+
+    /** Sign a transaction with all the wallet’s signers. */
+    fun sign(psbt: PartiallySignedBitcoinTransaction): Boolean {}
+
+    /** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */
+    fun listTransactions(): List<TransactionDetails> {}
+
+    /** Get the Bitcoin network the wallet is using. */
+    fun network(): Network {}
+
+    /** Sync the internal database with the blockchain. */
+    fun sync(blockchain: Blockchain, progress: Progress?) {}
+
+    /** Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
+    fun listUnspent(): List<LocalUtxo> {}
+}
+
+/**
+ * Class that logs at level INFO every update received (if any).
+ */
+class Progress {
+    /** Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an optional text message that can be displayed to the user. */
+    fun update(progress: Float, message: String?) {}
+}
+
+/**
+ * A transaction builder.
+ *
+ * After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction.
+ *
+ * Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
+ */
+class TxBuilder() {
+    /** Add data as an output using OP_RETURN. */
+    fun addData(data: List<UByte>): TxBuilder {}
+
+    /** Add a recipient to the internal list. */
+    fun addRecipient(script: Script, amount: ULong): TxBuilder {}
+
+    /** Set the list of recipients by providing a list of [AddressAmount]. */
+    fun setRecipients(recipients: List<ScriptAmount>): TxBuilder {}
+
+    /** Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent" utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details. */
+    fun addUnspendable(unspendable: OutPoint): TxBuilder {}
+
+    /** Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent. */
+    fun addUtxo(outpoint: OutPoint): TxBuilder {}
+
+    /**
+     * Add the list of outpoints to the internal list of UTXOs that must be spent. If an error
+     * occurs while adding any of the UTXOs then none of them are added and the error is returned.
+     * These have priority over the "unspendable" utxos, meaning that if a utxo is present both
+     * in the "utxos" and the "unspendable" list, it will be spent.
+     */
+    fun addUtxos(outpoints: List<OutPoint>): TxBuilder {}
+
+    /** Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
+    fun doNotSpendChange(): TxBuilder {}
+
+    /** Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid. */
+    fun manuallySelectedOnly(): TxBuilder {}
+
+    /** Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
+    fun onlySpendChange(): TxBuilder {}
+
+    /**
+     * Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos
+     * added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details.
+     */
+    fun unspendable(unspendable: List<OutPoint>): TxBuilder {}
+
+    /** Set a custom fee rate. */
+    fun feeRate(satPerVbyte: Float): TxBuilder {}
+
+    /** Set an absolute fee. */
+    fun feeAbsolute(feeAmount: ULong): TxBuilder {}
+
+    /** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */
+    fun drainWallet(): TxBuilder {}
+
+    /**
+     * Sets the address to drain excess coins to. Usually, when there are excess coins they are
+     * sent to a change address generated by the wallet. This option replaces the usual change address
+     * with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the
+     * drain output is not needed (the excess coins are too small) it will not be included in the resulting
+     * transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients
+     * with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any
+     * recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set
+     * [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option,
+     * you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees.
+     */
+    fun drainTo(address: String): TxBuilder {}
+
+    /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
+    fun enableRbf(): TxBuilder {}
+
+    /**
+     * Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
+     * contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
+     * is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
+     */
+    fun enableRbfWithSequence(nsequence: UInt): TxBuilder {}
+
+    /** Finish building the transaction. Returns a [TxBuilderResult]. */
+    fun finish(wallet: Wallet): TxBuilderResult {}
+}
+
+/**
+ * A object holding an ScriptPubKey and an amount.
+ *
+ * @property script The ScriptPubKey.
+ * @property amount The amount.
+ */
+data class AddressAmount (
+    var script: Script,
+    var amount: ULong
+)
+
+/**
+ * The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
+ */
+class BumpFeeTxBuilder() {
+    /**
+     * Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this scriptPubKey
+     * in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output
+     * to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is
+     * preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error
+     * if scriptPubkey can’t be found among the recipients of the transaction we are bumping.
+     */
+    fun allowShrinking(address: String): BumpFeeTxBuilder {}
+
+    /** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
+    fun enableRbf(): BumpFeeTxBuilder {}
+
+    /**
+     * Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
+     * contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
+     * is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
+     */
+    fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {}
+
+    /** Finish building the transaction. Returns a [TxBuilderResult]. */
+    fun finish(wallet: Wallet): TxBuilderResult {}
+}
+
+/**
+ * A BIP-32 derivation path.
+ *
+ * @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey]
+ * or [DescriptorPublicKey].
+ */
+class DerivationPath(path: String) {}
+
+/**
+ * An extended secret key.
+ *
+ * @param network The network this DescriptorSecretKey is to be used on.
+ * @param mnemonic The mnemonic.
+ * @param password The optional passphrase that can be provided as per BIP-39.
+ *
+ * @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample
+ * @sample org.bitcoindevkit.descriptorSecretKeyExtendSample
+ */
+class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String?) {
+    /** Derive a private descriptor at a given path. */
+    fun derive(path: DerivationPath): DescriptorSecretKey {}
+
+    /** Extend the private descriptor with a custom path. */
+    fun extend(path: DerivationPath): DescriptorSecretKey {}
+
+    /** Return the public version of the descriptor. */
+    fun asPublic(): DescriptorPublicKey {}
+
+    /* Return the raw private key as bytes. */
+    fun secretBytes(): List<UByte>
+
+    /** Return the private descriptor as a string. */
+    fun asString(): String {}
+}
+
+/**
+ * An extended public key.
+ *
+ * @param network The network this DescriptorPublicKey is to be used on.
+ * @param mnemonic The mnemonic.
+ * @param password The optional passphrase that can be provided as per BIP-39.
+ */
+class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) {
+    /** Derive a public descriptor at a given path. */
+    fun derive(path: DerivationPath): DescriptorSecretKey
+
+    /** Extend the public descriptor with a custom path. */
+    fun extend(path: DerivationPath): DescriptorSecretKey
+
+    /** Return the public descriptor as a string. */
+    fun asString(): String
+}
+
+/**
+ * An enum describing entropy length (aka word count) in the mnemonic.
+ */
+enum class WordCount {
+    /** 12 words mnemonic (128 bits entropy). */
+    WORDS12,
+
+    /** 15 words mnemonic (160 bits entropy). */
+    WORDS15,
+
+    /** 18 words mnemonic (192 bits entropy). */
+    WORDS18,
+
+    /** 21 words mnemonic (224 bits entropy). */
+    WORDS21,
+
+    /** 24 words mnemonic (256 bits entropy). */
+    WORDS24,
+}
+
+/**
+ * The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder].
+ *
+ * @property psbt The PSBT
+ * @property transactionDetails The transaction details.
+ *
+ * @sample org.bitcoindevkit.txBuilderResultSample1
+ * @sample org.bitcoindevkit.txBuilderResultSample2
+ */
+data class TxBuilderResult (
+    var psbt: PartiallySignedBitcoinTransaction,
+    var transactionDetails: TransactionDetails
+)
+
+/**
+ * A bitcoin script.
+ */
+class Script(rawOutputScript: List<UByte>)
+
+/**
+ * A bitcoin address.
+ *
+ * @param address The address in string format.
+ */
+class Address(address: String) {
+    /* Return the ScriptPubKey. */
+    fun scriptPubkey(): Script
+}
+
+/**
+ * Mnemonic phrases are a human-readable version of the private keys. Supported number of words are 12, 15, 18, 21 and 24.
+ *
+ * @constructor Generates Mnemonic with a random entropy.
+ * @param mnemonic The mnemonic as a string of space-separated words.
+ *
+ * @sample org.bitcoindevkit.mnemonicSample
+ */
+class Mnemonic(mnemonic: String) {
+    /* Returns Mnemonic as string */
+    fun asString(): String
+
+    /* Parse a Mnemonic from a given string. */
+    fun fromString(): Mnemonic
+
+    /*
+     * Create a new Mnemonic in the specified language from the given entropy. Entropy must be a
+     * multiple of 32 bits (4 bytes) and 128-256 bits in length.
+     */
+    fun fromEntropy(): Mnemonic
+}
diff --git a/bdk-android/build.gradle.kts b/bdk-android/build.gradle.kts
new file mode 100644
index 00000000..8304c9c9
--- /dev/null
+++ b/bdk-android/build.gradle.kts
@@ -0,0 +1,33 @@
+buildscript {
+    repositories {
+        google()
+    }
+    dependencies {
+        classpath("com.android.tools.build:gradle:7.1.2")
+    }
+}
+
+plugins {
+    id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
+}
+
+// These properties are required here so that the nexus publish-plugin
+// finds a staging profile with the correct group (group is otherwise set as "")
+// and knows whether to publish to a SNAPSHOT repository or not
+// https://github.com/gradle-nexus/publish-plugin#applying-the-plugin
+group = "org.bitcoindevkit"
+version = "0.12.0-SNAPSHOT"
+
+nexusPublishing {
+    repositories {
+        create("sonatype") {
+            nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
+            snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
+
+            val ossrhUsername: String? by project
+            val ossrhPassword: String? by project
+            username.set(ossrhUsername)
+            password.set(ossrhPassword)
+        }
+    }
+}
diff --git a/bdk-android/gradle.properties b/bdk-android/gradle.properties
new file mode 100644
index 00000000..58f55b98
--- /dev/null
+++ b/bdk-android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536m
+android.useAndroidX=true
+android.enableJetifier=true
+kotlin.code.style=official
diff --git a/bdk-android/gradle/wrapper/gradle-wrapper.jar b/bdk-android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
Binary files /dev/null and b/bdk-android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/bdk-android/gradle/wrapper/gradle-wrapper.properties b/bdk-android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..d2880ba8
--- /dev/null
+++ b/bdk-android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/bdk-android/gradlew b/bdk-android/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/bdk-android/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${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 "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# 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  ;; #(
+  MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/bdk-android/gradlew.bat b/bdk-android/gradlew.bat
new file mode 100644
index 00000000..107acd32
--- /dev/null
+++ b/bdk-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/bdk-android/lib/build.gradle.kts b/bdk-android/lib/build.gradle.kts
new file mode 100644
index 00000000..6164f29f
--- /dev/null
+++ b/bdk-android/lib/build.gradle.kts
@@ -0,0 +1,106 @@
+plugins {
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android") version "1.6.10"
+    id("maven-publish")
+    id("signing")
+
+    // Custom plugin to generate the native libs and bindings file
+    id("org.bitcoindevkit.plugins.generate-android-bindings")
+}
+
+repositories {
+    mavenCentral()
+    google()
+}
+
+android {
+    compileSdk = 31
+
+    defaultConfig {
+        minSdk = 21
+        targetSdk = 31
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles("consumer-rules.pro")
+    }
+
+    buildTypes {
+        getByName("release") {
+            isMinifyEnabled = false
+            proguardFiles(file("proguard-android-optimize.txt"), file("proguard-rules.pro"))
+        }
+    }
+
+    publishing {
+        singleVariant("release") {
+            withSourcesJar()
+            withJavadocJar()
+        }
+    }
+}
+
+dependencies {
+    implementation("net.java.dev.jna:jna:5.8.0@aar")
+    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
+    implementation("androidx.appcompat:appcompat:1.4.0")
+    implementation("androidx.core:core-ktx:1.7.0")
+    api("org.slf4j:slf4j-api:1.7.30")
+
+    androidTestImplementation("com.github.tony19:logback-android:2.0.0")
+    androidTestImplementation("androidx.test.ext:junit:1.1.3")
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
+    androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
+}
+
+afterEvaluate {
+    publishing {
+        publications {
+            create<MavenPublication>("maven") {
+                groupId = "org.bitcoindevkit"
+                artifactId = "bdk-android"
+                version = "0.12.0-SNAPSHOT"
+
+                from(components["release"])
+                pom {
+                    name.set("bdk-android")
+                    description.set("Bitcoin Dev Kit Kotlin language bindings.")
+                    url.set("https://bitcoindevkit.org")
+                    licenses {
+                        license {
+                            name.set("APACHE 2.0")
+                            url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE")
+                        }
+                        license {
+                            name.set("MIT")
+                            url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT")
+                        }
+                    }
+                    developers {
+                        developer {
+                            id.set("notmandatory")
+                            name.set("Steve Myers")
+                            email.set("notmandatory@noreply.github.org")
+                        }
+                        developer {
+                            id.set("artfuldev")
+                            name.set("Sudarsan Balaji")
+                            email.set("sudarsan.balaji@artfuldev.com")
+                        }
+                    }
+                    scm {
+                        connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git")
+                        developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git")
+                        url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master")
+                    }
+                }
+            }
+        }
+    }
+}
+
+signing {
+    val signingKeyId: String? by project
+    val signingKey: String? by project
+    val signingPassword: String? by project
+    useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
+    sign(publishing.publications)
+}
diff --git a/bdk-android/lib/proguard-rules.pro b/bdk-android/lib/proguard-rules.pro
new file mode 100644
index 00000000..264284c8
--- /dev/null
+++ b/bdk-android/lib/proguard-rules.pro
@@ -0,0 +1,28 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+# for JNA
+-dontwarn java.awt.*
+-keep class com.sun.jna.* { *; }
+-keep class org.bitcoindevkit.* { *; }
+-keepclassmembers class * extends org.bitcoindevkit.* { public *; }
+-keepclassmembers class * extends com.sun.jna.* { public *; }
diff --git a/bdk-android/lib/src/androidTest/assets/logback.xml b/bdk-android/lib/src/androidTest/assets/logback.xml
new file mode 100644
index 00000000..efb1f6b2
--- /dev/null
+++ b/bdk-android/lib/src/androidTest/assets/logback.xml
@@ -0,0 +1,14 @@
+<configuration>
+  <appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
+    <tagEncoder>
+      <pattern>%logger{12}</pattern>
+    </tagEncoder>
+    <encoder>
+      <pattern>[%-20thread] %msg</pattern>
+    </encoder>
+  </appender>
+
+  <root level="DEBUG">
+    <appender-ref ref="logcat" />
+  </root>
+</configuration>
diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt
new file mode 100644
index 00000000..1fb21320
--- /dev/null
+++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt
@@ -0,0 +1,81 @@
+package org.bitcoindevkit
+
+import org.junit.Assert.*
+import org.junit.Test
+import android.app.Application
+import android.content.Context.MODE_PRIVATE
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.runner.RunWith
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.io.File
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class AndroidLibTest {
+
+    private fun getTestDataDir(): String {
+        val context = ApplicationProvider.getApplicationContext<Application>()
+        return context.getDir("bdk-test", MODE_PRIVATE).toString()
+    }
+
+    private fun cleanupTestDataDir(testDataDir: String) {
+        File(testDataDir).deleteRecursively()
+    }
+
+    class LogProgress : Progress {
+        private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java)
+
+        override fun update(progress: Float, message: String?) {
+            log.debug("Syncing...")
+        }
+    }
+
+    private val descriptor =
+        "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
+
+    private val databaseConfig = DatabaseConfig.Memory
+
+    private val blockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            "ssl://electrum.blockstream.info:60002",
+            null,
+            5u,
+            null,
+            100u
+        )
+    )
+
+    @Test
+    fun memoryWalletNewAddress() {
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val address = wallet.getAddress(AddressIndex.NEW).address
+        assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
+    }
+
+    @Test
+    fun memoryWalletSyncGetBalance() {
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val blockchain = Blockchain(blockchainConfig)
+        wallet.sync(blockchain, LogProgress())
+        val balance: Balance = wallet.getBalance()
+        assertTrue(balance.total > 0u)
+    }
+
+    @Test
+    fun sqliteWalletSyncGetBalance() {
+        val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
+        val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val blockchain = Blockchain(blockchainConfig)
+        wallet.sync(blockchain, LogProgress())
+        val balance: Balance = wallet.getBalance()
+        assertTrue(balance.total > 0u)
+        cleanupTestDataDir(testDataDir)
+    }
+}
diff --git a/bdk-android/lib/src/main/AndroidManifest.xml b/bdk-android/lib/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..6aa0e82b
--- /dev/null
+++ b/bdk-android/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="org.bitcoindevkit">
+
+  <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
diff --git a/bdk-android/plugins/README.md b/bdk-android/plugins/README.md
new file mode 100644
index 00000000..40025f91
--- /dev/null
+++ b/bdk-android/plugins/README.md
@@ -0,0 +1,17 @@
+# Readme
+The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by `bdk-android`, and building the language bindings files.
+
+The plugin is applied to the `build.gradle.kts` file in `bdk-android` through the `plugins` block:
+```kotlin
+// bdk-android
+plugins {
+    id("org.bitcoindevkit.plugins.generate-android-bindings")
+}
+```
+
+It adds a series of tasks which are brought together into an aggregate task called `buildAndroidLib`. 
+
+This aggregate task:
+1. Builds the native libraries using `bdk-ffi`
+2. Places them in the correct resource directories
+3. Builds the bindings file
diff --git a/bdk-android/plugins/build.gradle.kts b/bdk-android/plugins/build.gradle.kts
new file mode 100644
index 00000000..37737bc5
--- /dev/null
+++ b/bdk-android/plugins/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+    id("java-gradle-plugin")
+    `kotlin-dsl`
+}
+
+gradlePlugin {
+    plugins {
+        create("uniFfiAndroidBindings") {
+            id = "org.bitcoindevkit.plugins.generate-android-bindings"
+            implementationClass = "org.bitcoindevkit.plugins.UniFfiAndroidPlugin"
+        }
+    }
+}
diff --git a/bdk-android/plugins/settings.gradle.kts b/bdk-android/plugins/settings.gradle.kts
new file mode 100644
index 00000000..ca62a7a7
--- /dev/null
+++ b/bdk-android/plugins/settings.gradle.kts
@@ -0,0 +1,8 @@
+dependencyResolutionManagement {
+    repositories {
+        mavenCentral()
+        google()
+    }
+}
+
+// include(":plugins")
diff --git a/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt b/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt
new file mode 100644
index 00000000..8f586b65
--- /dev/null
+++ b/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt
@@ -0,0 +1,14 @@
+package org.bitcoindevkit.plugins
+
+
+val operatingSystem: OS = when {
+    System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
+    System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
+    else -> OS.OTHER
+}
+
+enum class OS {
+    MAC,
+    LINUX,
+    OTHER,
+}
diff --git a/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiAndroidPlugin.kt b/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiAndroidPlugin.kt
new file mode 100644
index 00000000..4d2b16d9
--- /dev/null
+++ b/bdk-android/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiAndroidPlugin.kt
@@ -0,0 +1,180 @@
+package org.bitcoindevkit.plugins
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Exec
+import org.gradle.kotlin.dsl.environment
+import org.gradle.kotlin.dsl.getValue
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.register
+
+internal class UniFfiAndroidPlugin : Plugin<Project> {
+    override fun apply(target: Project): Unit = target.run {
+        val llvmArchPath = when (operatingSystem) {
+            OS.MAC -> "darwin-x86_64"
+            OS.LINUX -> "linux-x86_64"
+            OS.OTHER -> throw Error("Cannot build Android library from current architecture")
+        }
+
+        // arm64-v8a is the most popular hardware architecture for Android
+        val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
+
+            workingDir("${projectDir}/../../bdk-ffi")
+            val cargoArgs: MutableList<String> =
+                mutableListOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
+
+            executable("cargo")
+            args(cargoArgs)
+
+            // if ANDROID_NDK_ROOT is not set then set it to github actions default
+            if (System.getenv("ANDROID_NDK_ROOT") == null) {
+                environment(
+                    Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
+                )
+            }
+
+            environment(
+                // add build toolchain to PATH
+                Pair("PATH",
+                    "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
+
+                Pair("CFLAGS", "-D__ANDROID_API__=21"),
+                Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
+                Pair("CC", "aarch64-linux-android21-clang")
+            )
+
+            doLast {
+                println("Native library for bdk-android on aarch64 built successfully")
+            }
+        }
+
+        // the x86_64 version of the library is mostly used by emulators
+        val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
+
+            workingDir("${project.projectDir}/../../bdk-ffi")
+            val cargoArgs: MutableList<String> =
+                mutableListOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
+
+            executable("cargo")
+            args(cargoArgs)
+
+            // if ANDROID_NDK_ROOT is not set then set it to github actions default
+            if (System.getenv("ANDROID_NDK_ROOT") == null) {
+                environment(
+                    Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
+                )
+            }
+
+            environment(
+                // add build toolchain to PATH
+                Pair("PATH",
+                    "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
+
+                Pair("CFLAGS", "-D__ANDROID_API__=21"),
+                Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
+                Pair("CC", "x86_64-linux-android21-clang")
+            )
+
+            doLast {
+                println("Native library for bdk-android on x86_64 built successfully")
+            }
+        }
+
+        // armeabi-v7a version of the library for older 32-bit Android hardware
+        val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
+
+            workingDir("${project.projectDir}/../../bdk-ffi")
+            val cargoArgs: MutableList<String> =
+                mutableListOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
+
+            executable("cargo")
+            args(cargoArgs)
+
+            // if ANDROID_NDK_ROOT is not set then set it to github actions default
+            if (System.getenv("ANDROID_NDK_ROOT") == null) {
+                environment(
+                    Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
+                )
+            }
+
+            environment(
+                // add build toolchain to PATH
+                Pair("PATH",
+                    "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
+
+                Pair("CFLAGS", "-D__ANDROID_API__=21"),
+                Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER",
+                    "armv7a-linux-androideabi21-clang"),
+                Pair("CC", "armv7a-linux-androideabi21-clang")
+            )
+
+            doLast {
+                println("Native library for bdk-android on armv7 built successfully")
+            }
+        }
+
+        // move the native libs build by cargo from target/<architecture>/release/
+        // to their place in the bdk-android library
+        // the task only copies the available binaries built using the buildAndroid<architecture>Binary tasks
+        val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") {
+
+            dependsOn(buildAndroidAarch64Binary)
+
+            into("${project.projectDir}/../lib/src/main/jniLibs/")
+
+            into("arm64-v8a") {
+                from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
+            }
+
+            into("x86_64") {
+                from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
+            }
+
+            into("armeabi-v7a") {
+                from("${project.projectDir}/../../target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
+            }
+
+            doLast {
+                println("Native binaries for Android moved to ./lib/src/main/jniLibs/")
+            }
+        }
+
+        // generate the bindings using the bdk-ffi-bindgen tool located in the bdk-ffi submodule
+        val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
+            dependsOn(moveNativeAndroidLibs)
+
+            workingDir("${project.projectDir}/../../bdk-ffi")
+            executable("cargo")
+            args(
+                "run",
+                "--package",
+                "bdk-ffi-bindgen",
+                "--",
+                "--language",
+                "kotlin",
+                "--out-dir",
+                "../bdk-android/lib/src/main/kotlin"
+            )
+
+            doLast {
+                println("Android bindings file successfully created")
+            }
+        }
+
+        // create an aggregate task which will run the required tasks to build the Android libs in order
+        // the task will also appear in the printout of the ./gradlew tasks task with group and description
+        tasks.register("buildAndroidLib") {
+            group = "Bitcoindevkit"
+            description = "Aggregate task to build Android library"
+
+            dependsOn(
+                buildAndroidAarch64Binary,
+                buildAndroidX86_64Binary,
+                buildAndroidArmv7Binary,
+                moveNativeAndroidLibs,
+                generateAndroidBindings
+            )
+        }
+    }
+}
diff --git a/bdk-android/settings.gradle.kts b/bdk-android/settings.gradle.kts
new file mode 100644
index 00000000..ef7d1eb2
--- /dev/null
+++ b/bdk-android/settings.gradle.kts
@@ -0,0 +1,4 @@
+rootProject.name = "bdk-android"
+
+include(":lib")
+includeBuild("plugins")
diff --git a/bdk-ffi/bdk-ffi-bindgen/Cargo.toml b/bdk-ffi-bindgen/Cargo.toml
similarity index 100%
rename from bdk-ffi/bdk-ffi-bindgen/Cargo.toml
rename to bdk-ffi-bindgen/Cargo.toml
diff --git a/bdk-ffi/bdk-ffi-bindgen/src/main.rs b/bdk-ffi-bindgen/src/main.rs
similarity index 100%
rename from bdk-ffi/bdk-ffi-bindgen/src/main.rs
rename to bdk-ffi-bindgen/src/main.rs
diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml
index 63cd96fd..efac866a 100644
--- a/bdk-ffi/Cargo.toml
+++ b/bdk-ffi/Cargo.toml
@@ -5,10 +5,6 @@ authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.ba
 edition = "2018"
 license = "MIT OR Apache-2.0"
 
-[workspace]
-members = [".","bdk-ffi-bindgen"]
-default-members = [".", "bdk-ffi-bindgen"]
-
 [lib]
 crate-type = ["staticlib", "cdylib"]
 name = "bdkffi"
@@ -21,11 +17,3 @@ uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
 
 [build-dependencies]
 uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
-
-[profile.release-smaller]
-inherits = "release"
-opt-level = 'z'     # Optimize for size.
-lto = true          # Enable Link Time Optimization
-codegen-units = 1   # Reduce number of codegen units to increase optimizations.
-panic = 'abort'     # Abort on panic
-strip = true        # Strip symbols from binary*
diff --git a/bdk-ffi/README.md b/bdk-ffi/README.md
deleted file mode 100644
index c47fd5c3..00000000
--- a/bdk-ffi/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Native language bindings for BDK
-
-<p>
-    <a href="https://github.com/bitcoindevkit/bdk-ffi/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
-    <a href="https://github.com/bitcoindevkit/bdk-ffi/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk-ffi/workflows/CI/badge.svg"></a>
-    <a href="https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html"><img alt="Rustc Version 1.61.0+" src="https://img.shields.io/badge/rustc-1.61.0%2B-lightgrey.svg"/></a>
-    <a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
-  </p>
-
-The workspace in this repository creates the `libbdkffi` multi-language library for the rust based 
-[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for 
-generating the actual language binding code used to access the `libbdkffi` library.
-
-Each supported language has its own repository that includes this project as a [git submodule]. 
-The rust code in this project is a wrapper around the [bdk] library to expose it's APIs in a 
-uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
-
-## Supported target languages and platforms
-
-The below repositories include instructions for using, building, and publishing the native 
-language binding for [bdk] supported by this project.
-
-| Language | Platform     | Repository   |
-| -------- | ------------ | ------------ |
-| Kotlin   | jvm          | [bdk-kotlin] |
-| Kotlin   | android      | [bdk-kotlin] |
-| Swift    | iOS, macOS   | [bdk-swift]  |
-| Python   | linux, macOS | [bdk-python] |
-
-## Language bindings generator tool
-
-Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages. 
-To run `bdk-ffi-bindgen` and see the available options use the command:
-```shell
-cargo run -p bdk-ffi-bindgen -- --help
-```
-
-[bdk]: https://github.com/bitcoindevkit/bdk
-[Bitcoin Dev Kit]: https://github.com/bitcoindevkit
-[git submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
-[uniffi-rs]: https://github.com/mozilla/uniffi-rs
-
-[bdk-kotlin]: https://github.com/bitcoindevkit/bdk-kotlin
-[bdk-swift]: https://github.com/bitcoindevkit/bdk-swift
-[bdk-python]: https://github.com/bitcoindevkit/bdk-python
-
-## Contributing
-
-### Adding new structs and functions
-
-See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
-
-#### For pass by value objects
-
-1. create new rust struct with only fields that are supported UniFFI types
-1. update mapping `bdk.udl` file with new `dictionary`
-
-#### For pass by reference values 
-
-1. create wrapper rust struct/impl with only fields that are `Sync + Send`
-1. update mapping `bdk.udl` file with new `interface`
-
-## Goals
-
-1. Language bindings should feel idiomatic in target languages/platforms
-1. Adding new targets should be easy
-1. Getting up and running should be easy
-1. Contributing should be easy
-1. Get it right, then automate
-
-## Thanks
-
-This project is made possible thanks to the wonderful work by the [mozilla/uniffi-rs] team.
-
-[mozilla/uniffi-rs]: https://github.com/mozilla/uniffi-rs
diff --git a/bdk-jvm/build.gradle.kts b/bdk-jvm/build.gradle.kts
new file mode 100644
index 00000000..c9807ba2
--- /dev/null
+++ b/bdk-jvm/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+    id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
+}
+
+// These properties are required here so that the nexus publish-plugin
+// finds a staging profile with the correct group (group is otherwise set as "")
+// and knows whether to publish to a SNAPSHOT repository or not
+// https://github.com/gradle-nexus/publish-plugin#applying-the-plugin
+group = "org.bitcoindevkit"
+version = "0.12.0-SNAPSHOT"
+
+nexusPublishing {
+    repositories {
+        create("sonatype") {
+            nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
+            snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
+
+            val ossrhUsername: String? by project
+            val ossrhPassword: String? by project
+            username.set(ossrhUsername)
+            password.set(ossrhPassword)
+        }
+    }
+}
diff --git a/bdk-jvm/gradle.properties b/bdk-jvm/gradle.properties
new file mode 100644
index 00000000..e12e896c
--- /dev/null
+++ b/bdk-jvm/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536m
+android.enableJetifier=true
+kotlin.code.style=official
diff --git a/bdk-jvm/gradle/wrapper/gradle-wrapper.jar b/bdk-jvm/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
Binary files /dev/null and b/bdk-jvm/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/bdk-jvm/gradle/wrapper/gradle-wrapper.properties b/bdk-jvm/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..d2880ba8
--- /dev/null
+++ b/bdk-jvm/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/bdk-jvm/gradlew b/bdk-jvm/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/bdk-jvm/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${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 "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# 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  ;; #(
+  MSYS* | 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" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/bdk-jvm/gradlew.bat b/bdk-jvm/gradlew.bat
new file mode 100644
index 00000000..107acd32
--- /dev/null
+++ b/bdk-jvm/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/bdk-jvm/lib/build.gradle.kts b/bdk-jvm/lib/build.gradle.kts
new file mode 100644
index 00000000..b8ab1704
--- /dev/null
+++ b/bdk-jvm/lib/build.gradle.kts
@@ -0,0 +1,100 @@
+import org.gradle.api.tasks.testing.logging.TestExceptionFormat.*
+import org.gradle.api.tasks.testing.logging.TestLogEvent.*
+
+plugins {
+    id("org.jetbrains.kotlin.jvm") version "1.6.10"
+    id("java-library")
+    id("maven-publish")
+    id("signing")
+
+    // Custom plugin to generate the native libs and bindings file
+    id("org.bitcoindevkit.plugins.generate-jvm-bindings")
+}
+
+repositories {
+    mavenCentral()
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+    withSourcesJar()
+    withJavadocJar()
+}
+
+tasks.withType<Test> {
+    useJUnitPlatform()
+
+    testLogging {
+        events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
+        exceptionFormat = FULL
+        showExceptions = true
+        showCauses = true
+        showStackTraces = true
+    }
+}
+
+dependencies {
+    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
+    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
+    implementation("net.java.dev.jna:jna:5.8.0")
+    api("org.slf4j:slf4j-api:1.7.30")
+    testImplementation("junit:junit:4.13.2")
+    testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
+    testImplementation("ch.qos.logback:logback-classic:1.2.3")
+    testImplementation("ch.qos.logback:logback-core:1.2.3")
+}
+
+afterEvaluate {
+    publishing {
+        publications {
+            create<MavenPublication>("maven") {
+                groupId = "org.bitcoindevkit"
+                artifactId = "bdk-jvm"
+                version = "0.12.0-SNAPSHOT"
+
+                from(components["java"])
+                pom {
+                    name.set("bdk-jvm")
+                    description.set("Bitcoin Dev Kit Kotlin language bindings.")
+                    url.set("https://bitcoindevkit.org")
+                    licenses {
+                        license {
+                            name.set("APACHE 2.0")
+                            url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-APACHE")
+                        }
+                        license {
+                            name.set("MIT")
+                            url.set("https://github.com/bitcoindevkit/bdk/blob/master/LICENSE-MIT")
+                        }
+                    }
+                    developers {
+                        developer {
+                            id.set("notmandatory")
+                            name.set("Steve Myers")
+                            email.set("notmandatory@noreply.github.org")
+                        }
+                        developer {
+                            id.set("artfuldev")
+                            name.set("Sudarsan Balaji")
+                            email.set("sudarsan.balaji@artfuldev.com")
+                        }
+                    }
+                    scm {
+                        connection.set("scm:git:github.com/bitcoindevkit/bdk-ffi.git")
+                        developerConnection.set("scm:git:ssh://github.com/bitcoindevkit/bdk-ffi.git")
+                        url.set("https://github.com/bitcoindevkit/bdk-ffi/tree/master")
+                    }
+                }
+            }
+        }
+    }
+}
+
+signing {
+    val signingKeyId: String? by project
+    val signingKey: String? by project
+    val signingPassword: String? by project
+    useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
+    sign(publishing.publications)
+}
diff --git a/bdk-jvm/plugins/README.md b/bdk-jvm/plugins/README.md
new file mode 100644
index 00000000..b80e5d2f
--- /dev/null
+++ b/bdk-jvm/plugins/README.md
@@ -0,0 +1,16 @@
+# Readme
+The purpose of this directory is to host the Gradle plugin that adds tasks for building the native binaries required by bdk-jvm, and building the language bindings files.
+
+The plugin is applied to the `build.gradle.kts` file through the `plugins` block:
+```kotlin
+plugins {
+    id("org.bitcoindevkit.plugin.generate-jvm-bindings")
+}
+```
+
+The plugin adds a series of tasks which are brought together into an aggregate task called `buildJvmLib` for `bdk-jvm`. 
+
+This aggregate task:
+1. Builds the native library(ies) using `bdk-ffi`
+2. Places it in the correct resource directory
+3. Builds the bindings file
diff --git a/bdk-jvm/plugins/build.gradle.kts b/bdk-jvm/plugins/build.gradle.kts
new file mode 100644
index 00000000..91dfb288
--- /dev/null
+++ b/bdk-jvm/plugins/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+    id("java-gradle-plugin")
+    `kotlin-dsl`
+}
+
+gradlePlugin {
+    plugins {
+        create("uniFfiJvmBindings") {
+            id = "org.bitcoindevkit.plugins.generate-jvm-bindings"
+            implementationClass = "org.bitcoindevkit.plugins.UniFfiJvmPlugin"
+        }
+    }
+}
diff --git a/bdk-jvm/plugins/settings.gradle.kts b/bdk-jvm/plugins/settings.gradle.kts
new file mode 100644
index 00000000..ca62a7a7
--- /dev/null
+++ b/bdk-jvm/plugins/settings.gradle.kts
@@ -0,0 +1,8 @@
+dependencyResolutionManagement {
+    repositories {
+        mavenCentral()
+        google()
+    }
+}
+
+// include(":plugins")
diff --git a/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt b/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt
new file mode 100644
index 00000000..8f586b65
--- /dev/null
+++ b/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/Enums.kt
@@ -0,0 +1,14 @@
+package org.bitcoindevkit.plugins
+
+
+val operatingSystem: OS = when {
+    System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
+    System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
+    else -> OS.OTHER
+}
+
+enum class OS {
+    MAC,
+    LINUX,
+    OTHER,
+}
diff --git a/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiJvmPlugin.kt b/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiJvmPlugin.kt
new file mode 100644
index 00000000..3bad1859
--- /dev/null
+++ b/bdk-jvm/plugins/src/main/kotlin/org/bitcoindevkit/plugins/UniFfiJvmPlugin.kt
@@ -0,0 +1,123 @@
+package org.bitcoindevkit.plugins
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.Exec
+import org.gradle.kotlin.dsl.getValue
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.register
+
+internal class UniFfiJvmPlugin : Plugin<Project> {
+    override fun apply(target: Project): Unit = target.run {
+
+        // register a task called buildJvmBinaries which will run something like
+        // cargo build --release --target aarch64-apple-darwin
+        val buildJvmBinaries by tasks.register<DefaultTask>("buildJvmBinaries") {
+            if (operatingSystem == OS.MAC) {
+                exec {
+                    workingDir("${project.projectDir}/../../bdk-ffi")
+                    executable("cargo")
+                    val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-apple-darwin")
+                    args(cargoArgs)
+                }
+                exec {
+                    workingDir("${project.projectDir}/../../bdk-ffi")
+                    executable("cargo")
+                    val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
+                    args(cargoArgs)
+                }
+            } else if(operatingSystem == OS.LINUX) {
+                exec {
+                    workingDir("${project.projectDir}/../../bdk-ffi")
+                    executable("cargo")
+                    val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
+                    args(cargoArgs)
+                }
+            }
+        }
+
+        // move the native libs build by cargo from target/.../release/
+        // to their place in the bdk-jvm library
+        val moveNativeJvmLibs by tasks.register<DefaultTask>("moveNativeJvmLibs") {
+
+            // dependsOn(buildJvmBinaryX86_64MacOS, buildJvmBinaryAarch64MacOS, buildJvmBinaryLinux)
+            dependsOn(buildJvmBinaries)
+
+            data class CopyMetadata(val targetDir: String, val resDir: String, val ext: String)
+            val libsToCopy: MutableList<CopyMetadata> = mutableListOf()
+
+            if (operatingSystem == OS.MAC) {
+                libsToCopy.add(
+                    CopyMetadata(
+                        targetDir = "aarch64-apple-darwin",
+                        resDir = "darwin-aarch64",
+                        ext = "dylib"
+                    )
+                )
+                libsToCopy.add(
+                    CopyMetadata(
+                        targetDir = "x86_64-apple-darwin",
+                        resDir = "darwin-x86-64",
+                        ext = "dylib"
+                    )
+                )
+            } else if (operatingSystem == OS.LINUX) {
+                libsToCopy.add(
+                    CopyMetadata(
+                        targetDir = "x86_64-unknown-linux-gnu",
+                        resDir = "linux-x86-64",
+                        ext = "so"
+                    )
+                )
+            }
+
+            libsToCopy.forEach {
+                doFirst {
+                    copy {
+                        with(it) {
+                            from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/libbdkffi.${this.ext}")
+                            into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
+                        }
+                    }
+                }
+            }
+        }
+
+        // generate the bindings using the bdk-ffi-bindgen tool created in the bdk-ffi submodule
+        val generateJvmBindings by tasks.register<Exec>("generateJvmBindings") {
+
+            dependsOn(moveNativeJvmLibs)
+
+            workingDir("${project.projectDir}/../../bdk-ffi")
+            executable("cargo")
+            args(
+                "run",
+                "--package",
+                "bdk-ffi-bindgen",
+                "--",
+                "--language",
+                "kotlin",
+                "--out-dir",
+                "../bdk-jvm/lib/src/main/kotlin"
+            )
+
+            doLast {
+                println("JVM bindings file successfully created")
+            }
+        }
+
+        // we need an aggregate task which will run the 3 required tasks to build the JVM libs in order
+        // the task will also appear in the printout of the ./gradlew tasks task with a group and description
+        tasks.register("buildJvmLib") {
+            group = "Bitcoindevkit"
+            description = "Aggregate task to build JVM library"
+
+            dependsOn(
+                buildJvmBinaries,
+                moveNativeJvmLibs,
+                generateJvmBindings
+            )
+        }
+    }
+}
diff --git a/bdk-jvm/settings.gradle.kts b/bdk-jvm/settings.gradle.kts
new file mode 100644
index 00000000..d4238f3a
--- /dev/null
+++ b/bdk-jvm/settings.gradle.kts
@@ -0,0 +1,4 @@
+rootProject.name = "bdk-jvm"
+
+include(":lib")
+includeBuild("plugins")


Test patch:
diff --git a/.github/workflows/test-android.yaml b/.github/workflows/test-android.yaml
new file mode 100644
index 00000000..5083f57f
--- /dev/null
+++ b/.github/workflows/test-android.yaml
@@ -0,0 +1,57 @@
+name: Test Android
+on:
+  push:
+    paths:
+      - "bdk-ffi/**"
+      - "bdk-android/**"
+  pull_request:
+    paths:
+      - "bdk-ffi/**"
+      - "bdk-android/**"
+
+env:
+  ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
+  # By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
+  # ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
+
+jobs:
+  build:
+    runs-on: ubuntu-20.04
+    steps:
+      - name: Install Android NDK 21.4.7075529
+        run: |
+          ANDROID_ROOT=/usr/local/lib/android
+          ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
+          SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
+          echo "y" | $SDKMANAGER "ndk;21.4.7075529"
+
+      - name: Check out PR branch
+        uses: actions/checkout@v2
+
+      - name: cache
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            ./target
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: temurin
+          java-version: 11
+
+      - name: Install rust android targets
+        run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
+
+      - name: Build bdk-android library
+        run: |
+          cd bdk-android
+          ./gradlew buildAndroidLib
+
+      - name: Run Android tests
+        run: |
+          cd bdk-android
+          ./gradlew test --console=rich
diff --git a/.github/workflows/test-jvm.yaml b/.github/workflows/test-jvm.yaml
new file mode 100644
index 00000000..9116887f
--- /dev/null
+++ b/.github/workflows/test-jvm.yaml
@@ -0,0 +1,42 @@
+name: Test JVM
+on:
+  push:
+    paths:
+      - "bdk-ffi/**"
+      - "bdk-jvm/**"
+  pull_request:
+    paths:
+      - "bdk-ffi/**"
+      - "bdk-jvm/**"
+
+jobs:
+  build:
+    runs-on: ubuntu-20.04
+    steps:
+      - name: Check out PR branch
+        uses: actions/checkout@v2
+
+      - name: cache
+        uses: actions/cache@v2
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            ./target
+          key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
+
+      - name: Set up JDK
+        uses: actions/setup-java@v2
+        with:
+          distribution: temurin
+          java-version: 11
+
+      - name: Build bdk-jvm library
+        run: |
+          cd bdk-jvm
+          ./gradlew buildJvmLib
+
+      - name: Run JVM tests
+        run: |
+          cd bdk-jvm
+          ./gradlew test --console=rich
diff --git a/api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt b/api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
new file mode 100644
index 00000000..34840ec5
--- /dev/null
+++ b/api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
@@ -0,0 +1,233 @@
+package org.bitcoindevkit
+
+fun networkSample() {
+    val wallet = Wallet(
+        descriptor = descriptor,
+        changeDescriptor = changeDescriptor,
+        network = Network.TESTNET,
+        databaseConfig = DatabaseConfig.Memory
+    )
+}
+
+fun balanceSample() {
+    object LogProgress : Progress {
+        override fun update(progress: Float, message: String?) {}
+    }
+
+    val memoryDatabaseConfig = DatabaseConfig.Memory
+    private val blockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            "ssl://electrum.blockstream.info:60002",
+            null,
+            5u,
+            null,
+            200u
+        )
+    )
+    val wallet = Wallet(descriptor, null, Network.TESTNET, memoryDatabaseConfig)
+    val blockchain = Blockchain(blockchainConfig)
+    wallet.sync(blockchain, LogProgress)
+
+    val balance: Balance = wallet.getBalance()
+    println("Total wallet balance is ${balance.total}")
+}
+
+fun electrumBlockchainConfigSample() {
+    val blockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            url = "ssl://electrum.blockstream.info:60002",
+            socks5 = null,
+            retry = 5u,
+            timeout = null,
+            stopGap = 200u
+        )
+    )
+}
+
+fun memoryDatabaseConfigSample() {
+    val memoryDatabaseConfig = DatabaseConfig.Memory
+}
+
+fun sqliteDatabaseConfigSample() {
+    val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
+}
+
+fun addressIndexSample() {
+    val wallet: Wallet = Wallet(
+        descriptor = descriptor,
+        changeDescriptor = changeDescriptor,
+        network = Network.TESTNET,
+        databaseConfig = DatabaseConfig.Memory
+    )
+
+    fun getLastUnusedAddress(): AddressInfo {
+        return wallet.getAddress(AddressIndex.LAST_UNUSED)
+    }
+}
+
+fun addressInfoSample() {
+    val wallet: Wallet = Wallet(
+        descriptor = descriptor,
+        changeDescriptor = changeDescriptor,
+        network = Network.TESTNET,
+        databaseConfig = DatabaseConfig.Memory
+    )
+
+    fun getLastUnusedAddress(): AddressInfo {
+        return wallet.getAddress(AddressIndex.NEW)
+    }
+
+    val newAddress: AddressInfo = getLastUnusedAddress()
+
+    println("New address at index ${newAddress.index} is ${newAddress.address}")
+}
+
+fun blockchainSample() {
+    val blockchainConfig: BlockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            electrumURL,
+            null,
+            5u,
+            null,
+            10u
+        )
+    )
+
+    val blockchain: Blockchain = Blockchain(blockchainConfig)
+
+    blockchain.broadcast(signedPsbt)
+}
+
+
+fun txBuilderResultSample1() {
+    val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
+    // TxBuilderResult is a data class, which means you can use destructuring declarations on it to
+    // open it up in its component parts
+    val (psbt, txDetails) = TxBuilder()
+        .addRecipient(faucetAddress.scriptPubkey(), 1000u)
+        .feeRate(1.2f)
+        .finish(wallet)
+
+    println("Txid is ${txDetails.txid}")
+    wallet.sign(psbt)
+}
+
+fun txBuilderResultSample2() {
+    val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
+    val txBuilderResult: TxBuilderResult = TxBuilder()
+        .addRecipient(faucetAddress.scriptPubkey(), 1000u)
+        .feeRate(1.2f)
+        .finish(wallet)
+
+    val psbt = txBuilderResult.psbt
+    val txDetails = txBuilderResult.transactionDetails
+
+    println("Txid is ${txDetails.txid}")
+    wallet.sign(psbt)
+}
+
+fun descriptorSecretKeyExtendSample() {
+    // The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path.
+
+    // val mnemonic: String = generateMnemonic(WordCount.WORDS12)
+    val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
+
+    // the initial DescriptorSecretKey will always be at the "master" node,
+    // i.e. the derivation path is empty
+    val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
+        network = Network.TESTNET,
+        mnemonic = mnemonic,
+        password = ""
+    )
+    println(bip32RootKey.asString())
+    // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/*
+
+    // the derive method will also automatically apply the wildcard (*) to your path,
+    // i.e the following will generate the typical testnet BIP84 external wallet path
+    // m/84h/1h/0h/0/*
+    val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0")
+    val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString()
+    println(externalExtendedKey)
+    // tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/*
+
+    // to create the descriptor you'll need to use this extended key in a descriptor function,
+    // i.e. wpkh(), tr(), etc.
+    val externalDescriptor = "wpkh($externalExtendedKey)"
+}
+
+fun descriptorSecretKeyDeriveSample() {
+    // The DescriptorSecretKey.derive() method allows you to derive an extended key for a given
+    // node in the derivation tree (for example to create an xpub for a particular account)
+
+    val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
+    val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
+        network = Network.TESTNET,
+        mnemonic = mnemonic,
+        password = ""
+    )
+
+    val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h")
+    val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0)
+    println(xpubAccount0.asString())
+    // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/*
+
+    val internalPath: DerivationPath = DerivationPath("m/0")
+    val externalExtendedKey = xpubAccount0.extend(internalPath).asString()
+    println(externalExtendedKey)
+    // [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/*
+
+    // to create the descriptor you'll need to use this extended key in a descriptor function,
+    // i.e. wpkh(), tr(), etc.
+    val externalDescriptor = "wpkh($externalExtendedKey)"
+}
+
+fun createTransaction() {
+    val wallet = BdkWallet(
+        descriptor = externalDescriptor,
+        changeDescriptor = internalDescriptor,
+        network = Network.TESTNET,
+        databaseConfig = memoryDatabaseConfig,
+    )
+    val blockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            "ssl://electrum.blockstream.info:60002",
+            null,
+            5u,
+            null,
+            200u
+        )
+    )
+
+    val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
+    val (psbt, txDetails) = TxBuilder()
+        .addRecipient(faucetAddress.scriptPubkey(), 1000u)
+        .feeRate(1.2f)
+        .finish(wallet)
+
+    wallet.sign(psbt)
+    blockchain.broadcast(psbt)
+}
+
+fun walletSample() {
+    val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)"
+    val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)"
+    val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
+
+    val wallet = BdkWallet(
+        descriptor = externalDescriptor,
+        changeDescriptor = internalDescriptor,
+        network = Network.TESTNET,
+        databaseConfig = sqliteDatabaseConfig,
+    )
+}
+
+fun mnemonicSample() {
+    val mnemonic0: Mnemonic = Mnemonic(WordCount.WORDS12)
+
+    val mnemonic1: Mnemonic = Mnemonic.fromString("scene change clap smart together mind wheel knee clip normal trial unusual")
+
+    val entropy: List<UByte> = listOf<UByte>(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)
+    val mnemonic2: Mnemonic = Mnemonic.fromEntropy(entropy)
+
+    println(mnemonic0.asString(), mnemonic1.asString(), mnemonic2.asString())
+}
diff --git a/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt b/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
new file mode 100644
index 00000000..a66f8cd7
--- /dev/null
+++ b/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
@@ -0,0 +1,73 @@
+package org.bitcoindevkit
+
+import org.junit.Assert.*
+import org.junit.Test
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.io.File
+import java.nio.file.Files
+
+/**
+ * Library test, which will execute on linux host.
+ */
+class JvmLibTest {
+
+    private fun getTestDataDir(): String {
+        return Files.createTempDirectory("bdk-test").toString()
+    }
+
+    private fun cleanupTestDataDir(testDataDir: String) {
+        File(testDataDir).deleteRecursively()
+    }
+
+    class LogProgress : Progress {
+        private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java)
+
+        override fun update(progress: Float, message: String?) {
+            log.debug("Syncing...")
+        }
+    }
+
+    private val descriptor =
+        "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
+
+    private val databaseConfig = DatabaseConfig.Memory
+
+    private val blockchainConfig = BlockchainConfig.Electrum(
+        ElectrumConfig(
+            "ssl://electrum.blockstream.info:60002",
+            null,
+            5u,
+            null,
+            100u
+        )
+    )
+
+    @Test
+    fun memoryWalletNewAddress() {
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val address = wallet.getAddress(AddressIndex.NEW).address
+        assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
+    }
+
+    @Test
+    fun memoryWalletSyncGetBalance() {
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val blockchain = Blockchain(blockchainConfig)
+        wallet.sync(blockchain, LogProgress())
+        val balance: Balance = wallet.getBalance()
+        assertTrue(balance.total > 0u)
+    }
+
+    @Test
+    fun sqliteWalletSyncGetBalance() {
+        val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
+        val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
+        val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
+        val blockchain = Blockchain(blockchainConfig)
+        wallet.sync(blockchain, LogProgress())
+        val balance: Balance = wallet.getBalance()
+        assertTrue(balance.total > 0u)
+        cleanupTestDataDir(testDataDir)
+    }
+}