Repository: wizardsardine/liana
Pull request #927 (2024-01-17 20:24:34)

commands: include missing amount in spend response
This PR follows a discussion around https://github.com/wizardsardine/liana/pull/873#issuecomment-1886715468.

The GUI uses the `InsufficientFunds` error to get the missing amount when the user is creating a new spend, but it is not straightforward to extract this information in a general way from the RPC error (see https://github.com/wizardsardine/liana/issues/822#issuecomment-1836482355) and instead the spend module's `create_spend` is currently used (see https://github.com/wizardsardine/liana/pull/863).

With this PR, the missing amount will be included in the `createspend` response rather than as an error.

These changes are based on suggestions from @darosior and @edouardparis.

In a follow-up PR, the GUI should revert to using the `createspend` command to calculate the amount left to select.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 41b615f0..f8e9c971 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -51,7 +51,7 @@ jobs:
       - uses: actions/checkout@v1
       - uses: actions-rs/toolchain@v1
         with:
-            toolchain: 1.75.0
+            toolchain: 1.65.0
             components: rustfmt, clippy
             override: true
       - name: rustfmt
@@ -68,7 +68,7 @@ jobs:
     strategy:
       matrix:
         toolchain:
-          - 1.65.0
+          - 1.65
           - nightly
         os:
           - ubuntu-latest
diff --git a/doc/API.md b/doc/API.md
index e0ea64d2..063eeb82 100644
--- a/doc/API.md
+++ b/doc/API.md
@@ -53,15 +53,14 @@ This command does not take any parameter for now.
 
 #### Response
 
-| Field                | Type          | Description                                                                                  |
-| -------------------- | ------------- | -------------------------------------------------------------------------------------------- |
+| Field                | Type    | Description                                                                                        |
+| -------------------- | ------- | -------------------------------------------------------------------------------------------------- |
 | `version`            | string        | Version following the [SimVer](http://www.simver.org/) format                                |
 | `network`            | string        | Answer can be `mainnet`, `testnet`, `regtest`                                                |
 | `block_height`       | integer       | The block height we are synced at.                                                           |
 | `sync`               | float         | The synchronization progress as percentage (`0 < sync < 1`)                                  |
 | `descriptors`        | object        | Object with the name of the descriptor as key and the descriptor string as value             |
 | `rescan_progress`    | float or null | Progress of an ongoing rescan as a percentage (between 0 and 1) if there is any              |
-| `timestamp`          | integer       | Unix timestamp of wallet creation date                                                       |
 
 ### `getnewaddress`
 
@@ -180,11 +179,18 @@ This command will refuse to create any output worth less than 5k sats.
 
 #### Response
 
+If the spend is created successfully, the following response will be received:
+
 | Field          | Type              | Description                                          |
 | -------------- | ----------------- | ---------------------------------------------------- |
 | `psbt`         | string            | PSBT of the spending transaction, encoded as base64. |
 | `warnings`     | list of string    | Warnings, if any, generated during spend creation.   |
 
+If there are insufficient funds to create the required spend, then the following response will be received:
+
+| Field          | Type              | Description                                          |
+| -------------- | ----------------- | ---------------------------------------------------- |
+| `missing`      | integer           | Additional sats required to create the spend.        |
 
 ### `updatespend`
 
@@ -295,9 +301,7 @@ allowed in order to replace this transaction using RBF (see https://github.com/b
 
 #### Response
 
-| Field          | Type      | Description                                          |
-| -------------- | --------- | ---------------------------------------------------- |
-| `psbt`         | string    | PSBT of the spending transaction, encoded as base64. |
+The response is the same as for [`createspend`](#createspend).
 
 ### `startrescan`
 
diff --git a/gui/Cargo.lock b/gui/Cargo.lock
index 17f0f2bd..0d738567 100644
--- a/gui/Cargo.lock
+++ b/gui/Cargo.lock
@@ -138,18 +138,6 @@ dependencies = [
  "version_check",
 ]
 
-[[package]]
-name = "ahash"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
-dependencies = [
- "cfg-if",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
 [[package]]
 name = "aho-corasick"
 version = "0.7.20"
@@ -165,12 +153,6 @@ version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
 
-[[package]]
-name = "allocator-api2"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
-
 [[package]]
 name = "android_system_properties"
 version = "0.1.5"
@@ -224,9 +206,8 @@ dependencies = [
 
 [[package]]
 name = "async-hwi"
-version = "0.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b9ba5d4bd59fea79a70a375da2fe077b2b17ce26ace805d6939275719ad66e"
+version = "0.0.13"
+source = "git+https://github.com/wizardsardine/async-hwi?branch=master#d49673778bf5e8f907686f71888db9cf92c85038"
 dependencies = [
  "async-trait",
  "bitbox-api",
@@ -251,7 +232,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -301,9 +282,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64"
-version = "0.21.6"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9"
+checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
 
 [[package]]
 name = "base64-compat"
@@ -328,9 +309,9 @@ checksum = "c0320167c3655e83f0415d52f39618902e449186ffc7dfb090f922f79675c316"
 
 [[package]]
 name = "bech32"
-version = "0.10.0-beta"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea"
+checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
 
 [[package]]
 name = "bip39"
@@ -366,9 +347,9 @@ checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
 
 [[package]]
 name = "bitbox-api"
-version = "0.2.3"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1c0e1d593628bd289304902364f69c24686734d1825c54e03d89e003aa87b4f"
+checksum = "bb3e44c693da4b4db46e2e3f2beb28479cb6a0bd4ebda12f1f22b39a48188f88"
 dependencies = [
  "async-trait",
  "base32",
@@ -392,30 +373,20 @@ dependencies = [
 
 [[package]]
 name = "bitcoin"
-version = "0.31.1"
+version = "0.30.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd00f3c09b5f21fb357abe32d29946eb8bb7a0862bae62c0b5e4a692acbbe73c"
+checksum = "b36f4c848f6bd9ff208128f08751135846cc23ae57d66ab10a22efff1c675f3c"
 dependencies = [
- "base64 0.21.6",
+ "base64 0.13.1",
  "bech32",
- "bitcoin-internals",
- "bitcoin_hashes 0.13.0",
+ "bitcoin-private",
+ "bitcoin_hashes 0.12.0",
  "core2",
- "hex-conservative",
  "hex_lit",
  "secp256k1",
  "serde",
 ]
 
-[[package]]
-name = "bitcoin-internals"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb"
-dependencies = [
- "serde",
-]
-
 [[package]]
 name = "bitcoin-private"
 version = "0.1.0"
@@ -435,17 +406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
 dependencies = [
  "bitcoin-private",
-]
-
-[[package]]
-name = "bitcoin_hashes"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b"
-dependencies = [
- "bitcoin-internals",
  "core2",
- "hex-conservative",
  "serde",
 ]
 
@@ -455,12 +416,6 @@ version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
-[[package]]
-name = "bitflags"
-version = "2.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
-
 [[package]]
 name = "blake2"
 version = "0.10.6"
@@ -508,7 +463,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -699,7 +654,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "block",
  "cocoa-foundation",
  "core-foundation",
@@ -715,7 +670,7 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "block",
  "core-foundation",
  "core-graphics-types",
@@ -736,9 +691,8 @@ dependencies = [
 
 [[package]]
 name = "coldcard"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a02245612d550816be0d18aaea125372568a2d3d2f673f7b313294543f0fe03d"
+version = "0.10.0"
+source = "git+https://github.com/darosior/rust-coldcard.git?branch=2401_api_feature_msrv#d817bd970995e7994c8d468f460c31141c47f7c6"
 dependencies = [
  "aes-ctr",
  "base58",
@@ -816,7 +770,7 @@ version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "core-foundation",
  "core-graphics-types",
  "foreign-types 0.3.2",
@@ -829,7 +783,7 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "core-foundation",
  "foreign-types 0.3.2",
  "libc",
@@ -1016,7 +970,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1043,7 +997,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "scratch",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1060,7 +1014,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1069,7 +1023,7 @@ version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "libloading",
  "winapi",
 ]
@@ -1381,9 +1335,9 @@ dependencies = [
 
 [[package]]
 name = "fallible-iterator"
-version = "0.3.0"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
 [[package]]
 name = "fallible-streaming-iterator"
@@ -1544,7 +1498,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1574,7 +1528,7 @@ version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "freetype-sys",
  "libc",
 ]
@@ -1647,7 +1601,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1911,7 +1865,7 @@ version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fc59e5f710e310e76e6707f86c561dd646f69a8876da9131703b2f717de818d"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "gpu-alloc-types",
 ]
 
@@ -1921,7 +1875,7 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
 ]
 
 [[package]]
@@ -1943,7 +1897,7 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "gpu-descriptor-types",
  "hashbrown 0.12.3",
 ]
@@ -1954,7 +1908,7 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
 ]
 
 [[package]]
@@ -2006,13 +1960,22 @@ dependencies = [
  "crunchy",
 ]
 
+[[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"
 dependencies = [
- "ahash 0.7.6",
+ "ahash",
 ]
 
 [[package]]
@@ -2026,18 +1989,14 @@ name = "hashbrown"
 version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
-dependencies = [
- "ahash 0.8.7",
- "allocator-api2",
-]
 
 [[package]]
 name = "hashlink"
-version = "0.8.4"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
 dependencies = [
- "hashbrown 0.14.0",
+ "hashbrown 0.11.2",
 ]
 
 [[package]]
@@ -2046,7 +2005,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "com-rs",
  "libc",
  "libloading",
@@ -2082,15 +2041,6 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "hex-conservative"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2"
-dependencies = [
- "core2",
-]
-
 [[package]]
 name = "hex_lit"
 version = "0.1.1"
@@ -2244,7 +2194,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "11e1942e28dedee756cc27e67e7a838cdc1e59fb6bf9627ec9f709ab3b135782"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "instant",
  "palette",
 ]
@@ -2296,7 +2246,7 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "338a6aff7db906537074ad0fe8b720cfdb9512cdfea43c628c76bd1cf50fdcc0"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "bytemuck",
  "glam",
  "iced_native",
@@ -2353,7 +2303,7 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "478803c56061f567ce5ddf223b20d11d3c118cc46bb0d0552370dc65cdc4cb9c"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "bytemuck",
  "encase",
  "futures",
@@ -2537,9 +2487,9 @@ dependencies = [
 
 [[package]]
 name = "jsonrpc"
-version = "0.17.0"
+version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26d9104d516092f092d97448787505881fdb6518293b2d6500bf9c180c839dd"
+checksum = "34efde8d2422fb79ed56db1d3aea8fa5b583351d15a26770cdee2f88813dd702"
 dependencies = [
  "base64 0.13.1",
  "minreq",
@@ -2656,9 +2606,9 @@ dependencies = [
 
 [[package]]
 name = "ledger_bitcoin_client"
-version = "0.4.0"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8946cd76087649eed46851965dd5d06c45b996b6cfbe4436640e0fa6b7c4766f"
+checksum = "53b3850bd03537874d58c09dcdd15cfab272b3e9d3381e57843b4f9e1c9ceae7"
 dependencies = [
  "async-trait",
  "bitcoin",
@@ -2668,7 +2618,7 @@ dependencies = [
 [[package]]
 name = "liana"
 version = "4.0.0"
-source = "git+https://github.com/wizardsardine/liana?branch=master#7ca6cc8dfa8dfdde805dc34fa408337f1a3291e2"
+source = "git+https://github.com/wizardsardine/liana?branch=master#b8f8d1b944120879986a71255bab19e5b342ecc8"
 dependencies = [
  "backtrace",
  "bdk_coin_select",
@@ -2676,7 +2626,7 @@ dependencies = [
  "dirs 5.0.0",
  "fern",
  "getrandom",
- "jsonrpc 0.17.0",
+ "jsonrpc 0.16.0",
  "log",
  "miniscript",
  "rdrand",
@@ -2692,6 +2642,7 @@ version = "4.0.0"
 dependencies = [
  "async-hwi",
  "backtrace",
+ "base64 0.13.1",
  "bitcoin_hashes 0.12.0",
  "chrono",
  "dirs 3.0.2",
@@ -2751,9 +2702,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.27.0"
+version = "0.24.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
+checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
 dependencies = [
  "cc",
  "pkg-config",
@@ -2947,7 +2898,7 @@ version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "block",
  "core-graphics-types",
  "foreign-types 0.3.2",
@@ -2969,13 +2920,13 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniscript"
-version = "11.0.0"
+version = "10.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86a23dd3ad145a980e231185d114399f25a0a307d2cd918010ddda6334323df9"
+checksum = "1eb102b66b2127a872dbcc73095b7b47aeb9d92f7b03c2b2298253ffc82c7594"
 dependencies = [
- "bech32",
  "bitcoin",
- "bitcoin-internals",
+ "bitcoin-private",
+ "hashbrown 0.11.2",
  "serde",
 ]
 
@@ -3043,7 +2994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5"
 dependencies = [
  "bit-set",
- "bitflags 1.3.2",
+ "bitflags",
  "codespan-reporting",
  "hexf-parse",
  "indexmap 1.9.3",
@@ -3071,7 +3022,7 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "jni-sys",
  "ndk-sys",
  "num_enum",
@@ -3129,7 +3080,7 @@ version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "cc",
  "cfg-if",
  "libc",
@@ -3142,7 +3093,7 @@ version = "0.24.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "cfg-if",
  "libc",
  "memoffset 0.6.5",
@@ -3155,7 +3106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
 dependencies = [
  "autocfg",
- "bitflags 1.3.2",
+ "bitflags",
  "cfg-if",
  "libc",
  "memoffset 0.6.5",
@@ -3167,7 +3118,7 @@ version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "cfg-if",
  "libc",
  "memoffset 0.7.1",
@@ -3348,9 +3299,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.19.0"
+version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
 name = "opaque-debug"
@@ -3621,7 +3572,7 @@ version = "0.17.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "crc32fast",
  "flate2",
  "miniz_oxide",
@@ -3786,7 +3737,7 @@ dependencies = [
  "itertools",
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -3929,7 +3880,7 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
 ]
 
 [[package]]
@@ -3938,7 +3889,7 @@ version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
 ]
 
 [[package]]
@@ -3981,7 +3932,7 @@ version = "0.11.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
 dependencies = [
- "base64 0.21.6",
+ "base64 0.21.0",
  "bytes",
  "encoding_rs",
  "futures-core",
@@ -4091,15 +4042,16 @@ dependencies = [
 
 [[package]]
 name = "rusqlite"
-version = "0.30.0"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
+checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags",
  "fallible-iterator",
  "fallible-streaming-iterator",
  "hashlink",
  "libsqlite3-sys",
+ "memchr",
  "smallvec",
 ]
 
@@ -4140,7 +4092,7 @@ version = "0.37.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
@@ -4166,7 +4118,7 @@ version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
 dependencies = [
- "base64 0.21.6",
+ "base64 0.21.0",
 ]
 
 [[package]]
@@ -4185,7 +4137,7 @@ version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "bytemuck",
  "smallvec",
  "ttf-parser",
@@ -4266,20 +4218,20 @@ dependencies = [
 
 [[package]]
 name = "secp256k1"
-version = "0.28.1"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f622567e3b4b38154fb8190bcf6b160d7a4301d70595a49195b48c116007a27"
+checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f"
 dependencies = [
- "bitcoin_hashes 0.13.0",
+ "bitcoin_hashes 0.12.0",
  "secp256k1-sys",
  "serde",
 ]
 
 [[package]]
 name = "secp256k1-sys"
-version = "0.9.2"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb"
+checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e"
 dependencies = [
  "cc",
 ]
@@ -4307,7 +4259,7 @@ checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -4341,7 +4293,7 @@ checksum = "aab92efb5cf60ad310548bc3f16fa6b0d950019cb7ed8ff41968c3d03721cf12"
 dependencies = [
  "CoreFoundation-sys",
  "IOKit-sys",
- "bitflags 1.3.2",
+ "bitflags",
  "cfg-if",
  "libudev",
  "mach 0.3.2",
@@ -4471,7 +4423,7 @@ version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "calloop",
  "dlib",
  "lazy_static",
@@ -4547,7 +4499,7 @@ version = "0.2.0+1.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "num-traits",
 ]
 
@@ -4649,9 +4601,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.43"
+version = "2.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4708,7 +4660,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -4843,7 +4795,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -5104,7 +5056,7 @@ version = "0.29.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "63b6bb4e62619d9f68aa2d8a823fea2bff302340a1f2d45c264d5b0be170832e"
 dependencies = [
- "base64 0.21.6",
+ "base64 0.21.0",
  "data-url",
  "flate2",
  "imagesize",
@@ -5197,7 +5149,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
  "wasm-bindgen-shared",
 ]
 
@@ -5231,7 +5183,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5263,7 +5215,7 @@ version = "0.29.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "downcast-rs",
  "libc",
  "nix 0.24.3",
@@ -5312,7 +5264,7 @@ version = "0.29.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "wayland-client",
  "wayland-commons",
  "wayland-scanner",
@@ -5394,7 +5346,7 @@ checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff"
 dependencies = [
  "arrayvec 0.7.4",
  "bit-vec",
- "bitflags 1.3.2",
+ "bitflags",
  "codespan-reporting",
  "fxhash",
  "log",
@@ -5419,7 +5371,7 @@ dependencies = [
  "arrayvec 0.7.4",
  "ash",
  "bit-set",
- "bitflags 1.3.2",
+ "bitflags",
  "block",
  "core-graphics-types",
  "d3d12",
@@ -5457,7 +5409,7 @@ version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32444e121b0bd00cb02c0de32fde457a9491bd44e03e7a5db6df9b1da2f6f110"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "js-sys",
  "web-sys",
 ]
@@ -5744,7 +5696,7 @@ version = "0.27.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags",
  "cocoa",
  "core-foundation",
  "core-graphics",
@@ -5860,26 +5812,6 @@ version = "0.13.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
 
-[[package]]
-name = "zerocopy"
-version = "0.7.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.43",
-]
-
 [[package]]
 name = "zeroize"
 version = "1.7.0"
@@ -5897,7 +5829,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.43",
+ "syn 2.0.29",
 ]
 
 [[package]]
diff --git a/gui/Cargo.toml b/gui/Cargo.toml
index 09d471c1..b21bf381 100644
--- a/gui/Cargo.toml
+++ b/gui/Cargo.toml
@@ -14,10 +14,11 @@ name = "liana-gui"
 path = "src/main.rs"
 
 [dependencies]
-async-hwi = "0.0.14"
+async-hwi = { git = "https://github.com/wizardsardine/async-hwi", branch = "master"}
 liana = { git = "https://github.com/wizardsardine/liana", branch = "master", default-features = false, features = ["nonblocking_shutdown"] }
 liana_ui = { path = "ui" }
 backtrace = "0.3"
+base64 = "0.13"
 hex = "0.4.3"
 
 iced = { version = "0.9", default-features= false, features = ["tokio", "glow", "svg", "qr_code", "image"] }
diff --git a/gui/src/app/state/psbt.rs b/gui/src/app/state/psbt.rs
index 9e8632ed..623a3a72 100644
--- a/gui/src/app/state/psbt.rs
+++ b/gui/src/app/state/psbt.rs
@@ -1,6 +1,5 @@
 use std::collections::{HashMap, HashSet};
 use std::path::PathBuf;
-use std::str::FromStr;
 use std::sync::Arc;
 
 use iced::Subscription;
@@ -655,7 +654,8 @@ impl Action for UpdateAction {
                     Ok(()) => {
                         self.success = true;
                         self.error = None;
-                        let psbt = Psbt::from_str(&self.updated.value).expect("Already checked");
+                        let psbt = Psbt::deserialize(&base64::decode(&self.updated.value).unwrap())
+                            .expect("Already checked");
                         for (i, input) in tx.psbt.inputs.iter_mut().enumerate() {
                             if tx
                                 .psbt
@@ -688,7 +688,10 @@ impl Action for UpdateAction {
             }
             Message::View(view::Message::ImportSpend(view::ImportSpendMessage::PsbtEdited(s))) => {
                 self.updated.value = s;
-                if let Ok(psbt) = Psbt::from_str(&self.updated.value) {
+                if let Some(psbt) = base64::decode(&self.updated.value)
+                    .ok()
+                    .and_then(|bytes| Psbt::deserialize(&bytes).ok())
+                {
                     self.updated.valid = tx.psbt.unsigned_tx.txid() == psbt.unsigned_tx.txid();
                 }
             }
@@ -696,7 +699,10 @@ impl Action for UpdateAction {
                 if self.updated.valid {
                     self.processing = true;
                     self.error = None;
-                    let updated = Psbt::from_str(&self.updated.value).expect("Already checked");
+                    let updated = Psbt::deserialize(
+                        &base64::decode(&self.updated.value).expect("Already checked"),
+                    )
+                    .unwrap();
                     return Command::perform(
                         async move { daemon.update_spend_tx(&updated).map_err(|e| e.into()) },
                         Message::Updated,
diff --git a/gui/src/app/state/psbts.rs b/gui/src/app/state/psbts.rs
index 943eb6b2..04458271 100644
--- a/gui/src/app/state/psbts.rs
+++ b/gui/src/app/state/psbts.rs
@@ -1,4 +1,3 @@
-use std::str::FromStr;
 use std::sync::Arc;
 
 use iced::{Command, Subscription};
@@ -191,13 +190,19 @@ impl ImportPsbtModal {
             }
             Message::View(view::Message::ImportSpend(view::ImportSpendMessage::PsbtEdited(s))) => {
                 self.imported.value = s;
-                self.imported.valid = Psbt::from_str(&self.imported.value).ok().is_some();
+                self.imported.valid = base64::decode(&self.imported.value)
+                    .ok()
+                    .and_then(|bytes| Psbt::deserialize(&bytes).ok())
+                    .is_some();
             }
             Message::View(view::Message::ImportSpend(view::ImportSpendMessage::Confirm)) => {
                 if self.imported.valid {
                     self.processing = true;
                     self.error = None;
-                    let imported = Psbt::from_str(&self.imported.value).expect("Already checked");
+                    let imported = Psbt::deserialize(
+                        &base64::decode(&self.imported.value).expect("Already checked"),
+                    )
+                    .unwrap();
                     return Command::perform(
                         async move { daemon.update_spend_tx(&imported).map_err(|e| e.into()) },
                         Message::Updated,
diff --git a/gui/src/app/state/spend/step.rs b/gui/src/app/state/spend/step.rs
index 886d831a..9230d96a 100644
--- a/gui/src/app/state/spend/step.rs
+++ b/gui/src/app/state/spend/step.rs
@@ -440,9 +440,9 @@ impl Step for DefineSpend {
                         !recipient.label.value.is_empty()
                             && Address::from_str(&recipient.address.value)
                                 .unwrap()
-                                .payload()
+                                .payload
                                 .matches_script_pubkey(&output.script_pubkey)
-                            && output.value.to_sat() == recipient.amount().unwrap()
+                            && output.value == recipient.amount().unwrap()
                     })
                     .map(|recipient| recipient.label.value.to_string())
                 {
@@ -517,7 +517,7 @@ impl Recipient {
         }
 
         if let Ok(address) = Address::from_str(&self.address.value) {
-            if amount <= address.payload().script_pubkey().dust_value() {
+            if amount <= address.payload.script_pubkey().dust_value() {
                 return Err(Error::Unexpected(
                     "Amount must be superior to script dust value".to_string(),
                 ));
diff --git a/gui/src/app/view/home.rs b/gui/src/app/view/home.rs
index b26ea223..9a3f2bcf 100644
--- a/gui/src/app/view/home.rs
+++ b/gui/src/app/view/home.rs
@@ -174,13 +174,13 @@ fn event_list_view(i: usize, event: &HistoryTransaction) -> Column<'_, Message>
                     col.push(event::confirmed_incoming_event(
                         label,
                         NaiveDateTime::from_timestamp_opt(t as i64, 0).unwrap(),
-                        &output.value,
+                        &Amount::from_sat(output.value),
                         Message::SelectSub(i, output_index),
                     ))
                 } else {
                     col.push(event::unconfirmed_incoming_event(
                         label,
-                        &output.value,
+                        &Amount::from_sat(output.value),
                         Message::SelectSub(i, output_index),
                     ))
                 }
@@ -190,13 +190,13 @@ fn event_list_view(i: usize, event: &HistoryTransaction) -> Column<'_, Message>
                 col.push(event::confirmed_outgoing_event(
                     label,
                     NaiveDateTime::from_timestamp_opt(t as i64, 0).unwrap(),
-                    &output.value,
+                    &Amount::from_sat(output.value),
                     Message::SelectSub(i, output_index),
                 ))
             } else {
                 col.push(event::unconfirmed_outgoing_event(
                     label,
-                    &output.value,
+                    &Amount::from_sat(output.value),
                     Message::SelectSub(i, output_index),
                 ))
             }
@@ -251,7 +251,7 @@ pub fn payment_view<'a>(
                 label::label_editable(vec![outpoint.clone()], tx.labels.get(&outpoint), H3_SIZE)
             })
             .push(Container::new(amount_with_size(
-                &tx.tx.output[output_index].value,
+                &Amount::from_sat(tx.tx.output[output_index].value),
                 H3_SIZE,
             )))
             .push(Space::with_height(H3_SIZE))
diff --git a/gui/src/app/view/psbt.rs b/gui/src/app/view/psbt.rs
index 081f37fe..4033181c 100644
--- a/gui/src/app/view/psbt.rs
+++ b/gui/src/app/view/psbt.rs
@@ -8,8 +8,8 @@ use iced::{
 use liana::{
     descriptors::{LianaPolicy, PathInfo, PathSpendInfo},
     miniscript::bitcoin::{
-        bip32::Fingerprint, blockdata::transaction::TxOut, Address, Network, OutPoint, Transaction,
-        Txid,
+        bip32::Fingerprint, blockdata::transaction::TxOut, Address, Amount, Network, OutPoint,
+        Transaction, Txid,
     },
 };
 
@@ -868,7 +868,7 @@ fn payment_view<'a>(
                     })
                     .width(Length::Fill),
                 )
-                .push(amount(&output.value)),
+                .push(amount(&Amount::from_sat(output.value))),
         )
         .push_maybe(addr.map(|addr| {
             Column::new()
@@ -932,7 +932,7 @@ fn change_view(output: &TxOut, network: Network) -> Element<Message> {
                         ),
                 ),
         )
-        .push(amount(&output.value))
+        .push(amount(&Amount::from_sat(output.value)))
         .into()
 }
 
diff --git a/gui/src/daemon/client/mod.rs b/gui/src/daemon/client/mod.rs
index 7f7670bc..e34f8ed5 100644
--- a/gui/src/daemon/client/mod.rs
+++ b/gui/src/daemon/client/mod.rs
@@ -110,7 +110,7 @@ impl<C: Client + Debug> Daemon for Lianad<C> {
     }
 
     fn update_spend_tx(&self, psbt: &Psbt) -> Result<(), DaemonError> {
-        let spend_tx = psbt.to_string();
+        let spend_tx = base64::encode(psbt.serialize());
         let _res: serde_json::value::Value = self.call("updatespend", Some(vec![spend_tx]))?;
         Ok(())
     }
diff --git a/gui/src/daemon/model.rs b/gui/src/daemon/model.rs
index f663afc1..366d5a49 100644
--- a/gui/src/daemon/model.rs
+++ b/gui/src/daemon/model.rs
@@ -70,9 +70,9 @@ impl SpendTx {
             |(change, spend), (i, output)| {
                 if !psbt.outputs[i].bip32_derivation.is_empty() {
                     change_indexes.push(i);
-                    (change + output.value, spend)
+                    (change + Amount::from_sat(output.value), spend)
                 } else {
-                    (change, spend + output.value)
+                    (change, spend + Amount::from_sat(output.value))
                 }
             },
         );
@@ -103,7 +103,7 @@ impl SpendTx {
             let mut inputs_amount = Amount::from_sat(0);
             for (i, input) in psbt.inputs.iter().enumerate() {
                 if let Some(utxo) = &input.witness_utxo {
-                    inputs_amount += utxo.value;
+                    inputs_amount += Amount::from_sat(utxo.value);
                 // we try to have it from the coin
                 } else if let Some(coin) = psbt
                     .unsigned_tx
@@ -285,9 +285,9 @@ impl HistoryTransaction {
             (Amount::from_sat(0), Amount::from_sat(0)),
             |(change, spend), (i, output)| {
                 if change_indexes.contains(&i) {
-                    (change + output.value, spend)
+                    (change + Amount::from_sat(output.value), spend)
                 } else {
-                    (change, spend + output.value)
+                    (change, spend + Amount::from_sat(output.value))
                 }
             },
         );
diff --git a/gui/src/installer/step/bitcoind.rs b/gui/src/installer/step/bitcoind.rs
index 57a5d1f2..6572d36d 100644
--- a/gui/src/installer/step/bitcoind.rs
+++ b/gui/src/installer/step/bitcoind.rs
@@ -493,7 +493,7 @@ impl DefineBitcoind {
                 let builder = match selected_auth_type {
                     RpcAuthType::CookieFile => {
                         let cookie_path = rpc_auth_vals.cookie_path.value;
-                        let cookie = std::fs::read_to_string(cookie_path).map_err(|e| {
+                        let cookie = std::fs::read_to_string(&cookie_path).map_err(|e| {
                             Error::Bitcoind(format!("Failed to read cookie file: {}", e))
                         })?;
                         SimpleHttpTransport::builder().cookie_auth(cookie)
diff --git a/gui/src/installer/step/descriptor.rs b/gui/src/installer/step/descriptor.rs
index 79e3606c..7816c4d5 100644
--- a/gui/src/installer/step/descriptor.rs
+++ b/gui/src/installer/step/descriptor.rs
@@ -5,7 +5,7 @@ use std::str::FromStr;
 use std::sync::{Arc, Mutex};
 
 use iced::Command;
-use liana::miniscript::bitcoin::bip32::Xpub;
+use liana::miniscript::bitcoin::bip32::ExtendedPubKey;
 use liana::{
     descriptors::{LianaDescriptor, LianaPolicy, PathInfo},
     miniscript::{
@@ -565,9 +565,9 @@ impl Step for DefineDescriptor {
 }
 
 fn new_multixkey_from_xpub(
-    xpub: DescriptorXKey<Xpub>,
+    xpub: DescriptorXKey<ExtendedPubKey>,
     derivation_index: usize,
-) -> DescriptorMultiXKey<Xpub> {
+) -> DescriptorMultiXKey<ExtendedPubKey> {
     DescriptorMultiXKey {
         origin: xpub.origin,
         xkey: xpub.xkey,
@@ -719,7 +719,7 @@ impl EditXpubModal {
                             .name
                             .clone()
                     })
-                    .unwrap_or_default(),
+                    .unwrap_or_else(String::new),
             },
             form_xpub: form::Value {
                 valid: true,
@@ -731,7 +731,7 @@ impl EditXpubModal {
                             .key
                             .to_string()
                     })
-                    .unwrap_or_default(),
+                    .unwrap_or_else(String::new),
             },
             keys,
             path_index,
diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs
index 81a0939e..ee29d616 100644
--- a/gui/src/installer/view.rs
+++ b/gui/src/installer/view.rs
@@ -1621,14 +1621,14 @@ pub fn key_list_view<'a>(
 ) -> Element<'a, Message> {
     let bttn = Button::new(if chosen {
         hw::selected_hardware_wallet(
-            kind.map(|k| k.to_string()).unwrap_or_default(),
+            kind.map(|k| k.to_string()).unwrap_or_else(String::new),
             None::<String>,
             fingerprint,
             Some(name),
         )
     } else {
         hw::supported_hardware_wallet(
-            kind.map(|k| k.to_string()).unwrap_or_default(),
+            kind.map(|k| k.to_string()).unwrap_or_else(String::new),
             None::<String>,
             fingerprint,
             Some(name),
diff --git a/gui/src/loader.rs b/gui/src/loader.rs
index 19f43033..99441b9a 100644
--- a/gui/src/loader.rs
+++ b/gui/src/loader.rs
@@ -95,6 +95,7 @@ impl Loader {
             .daemon_rpc_path
             .clone()
             .unwrap_or_else(|| socket_path(&datadir_path, network));
+        let network = network;
         (
             Loader {
                 network,
@@ -320,6 +321,7 @@ impl Loader {
                 let reader = BufReader::new(file);
                 let last_update_tip = reader
                     .lines()
+                    .into_iter()
                     .filter(|l| {
                         l.as_ref()
                             .map(|l| l.contains("UpdateTip") || l.contains("blockheaders"))
diff --git a/gui/src/signer.rs b/gui/src/signer.rs
index 4f700994..6d65515b 100644
--- a/gui/src/signer.rs
+++ b/gui/src/signer.rs
@@ -2,7 +2,7 @@ pub use liana::signer::SignerError;
 
 use liana::{
     miniscript::bitcoin::{
-        bip32::{DerivationPath, Fingerprint, Xpub},
+        bip32::{DerivationPath, ExtendedPubKey, Fingerprint},
         psbt::Psbt,
         secp256k1, Network,
     },
@@ -48,7 +48,7 @@ impl Signer {
         self.fingerprint
     }
 
-    pub fn get_extended_pubkey(&self, path: &DerivationPath) -> Xpub {
+    pub fn get_extended_pubkey(&self, path: &DerivationPath) -> ExtendedPubKey {
         self.key.xpub_at(path, &self.curve)
     }
 
diff --git a/gui/ui/Cargo.toml b/gui/ui/Cargo.toml
index 60bcd406..53c8c48e 100644
--- a/gui/ui/Cargo.toml
+++ b/gui/ui/Cargo.toml
@@ -9,5 +9,5 @@ edition = "2021"
 iced = { version = "0.9", default_features = false, features = ["svg", "image", "glow"] }
 iced_native = "0.10"
 iced_lazy = { version = "0.6"}
-bitcoin = "0.31"
+bitcoin = "0.30"
 chrono = "0.4"
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index d4fecbc4..037d5d05 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -294,7 +294,6 @@ impl DaemonControl {
                 main: self.config.main_descriptor.clone(),
             },
             rescan_progress,
-            timestamp: db_conn.timestamp(),
         }
     }
 
@@ -495,7 +494,7 @@ impl DaemonControl {
             psbt,
             has_change,
             warnings,
-        } = create_spend(
+        } = match create_spend(
             &self.config.main_descriptor,
             &self.secp,
             &mut tx_getter,
@@ -503,7 +502,15 @@ impl DaemonControl {
             &candidate_coins,
             SpendTxFees::Regular(feerate_vb),
             change_address,
-        )?;
+        ) {
+            Ok(res) => res,
+            Err(SpendCreationError::CoinSelection(e)) => {
+                return Ok(CreateSpendResult::InsufficientFunds { missing: e.missing });
+            }
+            Err(e) => {
+                return Err(e.into());
+            }
+        };
         for (addr, _) in destinations_checked {
             self.maybe_increase_next_deriv_index(&mut db_conn, &addr.info);
         }
@@ -511,7 +518,7 @@ impl DaemonControl {
             self.maybe_increase_next_deriv_index(&mut db_conn, &change_info);
         }
 
-        Ok(CreateSpendResult {
+        Ok(CreateSpendResult::Success {
             psbt,
             warnings: warnings.iter().map(|w| w.to_string()).collect(),
         })
@@ -829,17 +836,19 @@ impl DaemonControl {
                 change_address.clone(),
             ) {
                 Ok(psbt) => psbt,
-                // If we get a coin selection error due to insufficient funds and we want to cancel the
-                // transaction, then set all previous coins as mandatory and add confirmed coins as
-                // optional, unless we have already done this.
-                Err(SpendCreationError::CoinSelection(_))
-                    if is_cancel && candidate_coins.iter().all(|c| !c.must_select) =>
-                {
-                    for cand in candidate_coins.iter_mut() {
-                        cand.must_select = true;
+                Err(SpendCreationError::CoinSelection(e)) => {
+                    // If we get a coin selection error due to insufficient funds and we want to cancel the
+                    // transaction, then set all previous coins as mandatory and add confirmed coins as
+                    // optional, unless we have already done this.
+                    if is_cancel && candidate_coins.iter().all(|c| !c.must_select) {
+                        for cand in candidate_coins.iter_mut() {
+                            cand.must_select = true;
+                        }
+                        candidate_coins.extend(&confirmed_cands);
+                        continue;
+                    } else {
+                        return Ok(CreateSpendResult::InsufficientFunds { missing: e.missing });
                     }
-                    candidate_coins.extend(&confirmed_cands);
-                    continue;
                 }
                 Err(e) => {
                     return Err(e.into());
@@ -863,7 +872,7 @@ impl DaemonControl {
                     self.maybe_increase_next_deriv_index(&mut db_conn, &change_address.info);
                 }
 
-                return Ok(CreateSpendResult {
+                return Ok(CreateSpendResult::Success {
                     psbt: rbf_psbt,
                     warnings: warnings.iter().map(|w| w.to_string()).collect(),
                 });
@@ -1031,8 +1040,6 @@ pub struct GetInfoResult {
     pub descriptors: GetInfoDescriptors,
     /// The progress as a percentage (between 0 and 1) of an ongoing rescan if there is any
     pub rescan_progress: Option<f64>,
-    /// Timestamp at wallet creation date
-    pub timestamp: u32,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1111,10 +1118,16 @@ pub struct ListCoinsResult {
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
-pub struct CreateSpendResult {
-    #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_fromstr")]
-    pub psbt: Psbt,
-    pub warnings: Vec<String>,
+#[serde(untagged)]
+pub enum CreateSpendResult {
+    Success {
+        #[serde(serialize_with = "ser_to_string", deserialize_with = "deser_fromstr")]
+        psbt: Psbt,
+        warnings: Vec<String>,
+    },
+    InsufficientFunds {
+        missing: u64,
+    },
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1153,7 +1166,6 @@ mod tests {
     use super::*;
     use crate::{bitcoin::Block, database::BlockInfo, spend::InsaneFeeInfo, testutils::*};
 
-    use bdk_coin_select::InsufficientFunds;
     use bitcoin::{
         bip32::{self, ChildNumber},
         blockdata::transaction::{TxIn, TxOut, Version as TxVersion},
@@ -1345,9 +1357,7 @@ mod tests {
         // Insufficient funds for coin selection.
         assert!(matches!(
             control.create_spend(&destinations, &[], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         assert_eq!(
             control.create_spend(&destinations, &[dummy_op], 0, None),
@@ -1375,20 +1385,23 @@ mod tests {
         // and so we get a coin selection error due to insufficient funds.
         assert!(matches!(
             control.create_spend(&destinations, &[], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
-        let res = control
+        let (psbt, warnings) = if let CreateSpendResult::Success { psbt, warnings } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        assert!(res.psbt.inputs[0].non_witness_utxo.is_some());
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            (psbt, warnings)
+        } else {
+            panic!("expect successful spend creation")
+        };
+        assert!(psbt.inputs[0].non_witness_utxo.is_some());
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 1);
         assert_eq!(tx.input[0].previous_output, dummy_op);
         assert_eq!(tx.output.len(), 2);
         // It has change so no warnings expected.
-        assert!(res.warnings.is_empty());
+        assert!(warnings.is_empty());
         assert_eq!(
             tx.output[0].script_pubkey,
             dummy_addr.payload().script_pubkey()
@@ -1404,10 +1417,15 @@ mod tests {
         // Transaction is 1 in (P2WSH satisfaction), 2 outs. At 1sat/vb, it's 170 sats fees.
         // At 2sats/vb, it's twice that.
         assert_eq!(tx.output[1].value.to_sat(), 89_830);
-        let res = control
+        let psbt = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(&destinations, &[dummy_op], 2, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.output[1].value.to_sat(), 89_660);
 
         // A feerate of 555 won't trigger the sanity checks (they were previously not taking the
@@ -1419,16 +1437,12 @@ mod tests {
         // If we ask for a too high feerate, or a too large/too small output, it'll fail.
         assert!(matches!(
             control.create_spend(&destinations, &[dummy_op], 10_000, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         *destinations.get_mut(&dummy_addr).unwrap() = 100_001;
         assert!(matches!(
             control.create_spend(&destinations, &[dummy_op], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         *destinations.get_mut(&dummy_addr).unwrap() = 4_500;
         assert_eq!(
@@ -1453,10 +1467,15 @@ mod tests {
         // If we ask for a large, but valid, output we won't get a change output. 95_000 because we
         // won't create an output lower than 5k sats.
         *destinations.get_mut(&dummy_addr).unwrap() = 95_000;
-        let res = control
+        let (psbt, warnings) = if let CreateSpendResult::Success { psbt, warnings } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            (psbt, warnings)
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 1);
         assert_eq!(tx.input[0].previous_output, dummy_op);
         assert_eq!(tx.output.len(), 1);
@@ -1466,64 +1485,87 @@ mod tests {
         );
         assert_eq!(tx.output[0].value.to_sat(), 95_000);
         // change = 100_000 - 95_000 - /* fee without change */ 127 - /* extra fee for change output */ 43 = 4830
-        assert_eq!(res.warnings, vec!["Change amount of 4830 sats added to fee as it was too small to create a transaction output."]);
+        assert_eq!(warnings, vec!["Change amount of 4830 sats added to fee as it was too small to create a transaction output."]);
 
         // Increase the target value by the change amount and the warning will disappear.
         *destinations.get_mut(&dummy_addr).unwrap() = 95_000 + 4_830;
-        let res = control
+        let (psbt, warnings) = if let CreateSpendResult::Success { psbt, warnings } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            (psbt, warnings)
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
-        assert!(res.warnings.is_empty());
+        assert!(warnings.is_empty());
 
         // Now increase target also by the extra fee that was paying for change and we can still create the spend.
         *destinations.get_mut(&dummy_addr).unwrap() =
             95_000 + 4_830 + /* fee for change output */ 43;
-        let res = control
+        let (psbt, warnings) = if let CreateSpendResult::Success { psbt, warnings } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            (psbt, warnings)
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
-        assert!(res.warnings.is_empty());
+        assert!(warnings.is_empty());
 
         // Now increase the target by 1 more sat and we will have insufficient funds.
         *destinations.get_mut(&dummy_addr).unwrap() =
             95_000 + 4_830 + /* fee for change output */ 43 + 1;
         assert_eq!(
             control.create_spend(&destinations, &[dummy_op], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(InsufficientFunds { missing: 1 })
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { missing: 1 }),
         );
 
         // Now decrease the target so that the lost change is just 1 sat.
         *destinations.get_mut(&dummy_addr).unwrap() =
             100_000 - /* fee without change */ 127 - /* extra fee for change output */ 43 - 1;
-        let res = control
+        let warnings = if let CreateSpendResult::Success { warnings, .. } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
+            .unwrap()
+        {
+            warnings
+        } else {
+            panic!("expect successful spend creation")
+        };
         // Message uses "sat" instead of "sats" when value is 1.
-        assert_eq!(res.warnings, vec!["Change amount of 1 sat added to fee as it was too small to create a transaction output."]);
+        assert_eq!(warnings, vec!["Change amount of 1 sat added to fee as it was too small to create a transaction output."]);
 
         // Now decrease the target value so that we have enough for a change output.
         *destinations.get_mut(&dummy_addr).unwrap() =
             95_000 - /* fee without change */ 127 - /* extra fee for change output */ 43;
-        let res = control
+        let (psbt, warnings) = if let CreateSpendResult::Success { psbt, warnings } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            (psbt, warnings)
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 2);
         assert_eq!(tx.output[1].value.to_sat(), 5_000);
-        assert!(res.warnings.is_empty());
+        assert!(warnings.is_empty());
 
         // Now increase the target by 1 and we'll get a warning again, this time for 1 less than the dust threshold.
         *destinations.get_mut(&dummy_addr).unwrap() =
             95_000 - /* fee without change */ 127 - /* extra fee for change output */ 43 + 1;
-        let res = control
+        let warnings = if let CreateSpendResult::Success { warnings, .. } = control
             .create_spend(&destinations, &[dummy_op], 1, None)
-            .unwrap();
-        assert_eq!(res.warnings, vec!["Change amount of 4999 sats added to fee as it was too small to create a transaction output."]);
+            .unwrap()
+        {
+            warnings
+        } else {
+            panic!("expect successful spend creation")
+        };
+        assert_eq!(warnings, vec!["Change amount of 4999 sats added to fee as it was too small to create a transaction output."]);
 
         // Now if we mark the coin as spent, we won't create another Spend transaction containing
         // it.
@@ -1542,9 +1584,7 @@ mod tests {
         // and so we get a coin selection error due to insufficient funds.
         assert!(matches!(
             control.create_spend(&destinations, &[], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
 
         // We'd bail out if they tried to create a transaction with a too high feerate.
@@ -1594,18 +1634,14 @@ mod tests {
         // Coin selection error due to insufficient funds.
         assert!(matches!(
             control.create_spend(&destinations, &[], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         // Set destination amount equal to value of confirmed coins.
         *destinations.get_mut(&dummy_addr).unwrap() = 80_000;
         // Coin selection error occurs due to insufficient funds to pay fee.
         assert!(matches!(
             control.create_spend(&destinations, &[], 1, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         let confirmed_op_2 = bitcoin::OutPoint {
             txid: confirmed_op_1.txid,
@@ -1626,8 +1662,14 @@ mod tests {
             spend_block: None,
         }]);
         // First, create a transaction using auto coin selection.
-        let res_auto = control.create_spend(&destinations, &[], 1, None).unwrap();
-        let tx_auto = res_auto.psbt.unsigned_tx;
+        let psbt = if let CreateSpendResult::Success { psbt, .. } =
+            control.create_spend(&destinations, &[], 1, None).unwrap()
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx_auto = psbt.unsigned_tx;
         let mut tx_prev_outpoints = tx_auto
             .input
             .iter()
@@ -1645,10 +1687,15 @@ mod tests {
         assert_eq!(tx_auto.output[0].value, Amount::from_sat(80_000));
 
         // Create a second transaction using manual coin selection.
-        let res_manual = control
+        let psbt = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(&destinations, &[confirmed_op_1, confirmed_op_2], 1, None)
-            .unwrap();
-        let tx_manual = res_manual.psbt.unsigned_tx;
+            .unwrap()
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx_manual = psbt.unsigned_tx;
         // Check that manual and auto selection give same outputs (including change).
         assert_eq!(tx_auto.output, tx_manual.output);
         // Check inputs are also the same. Need to sort as order is not guaranteed by `create_spend`.
@@ -1680,15 +1727,18 @@ mod tests {
         let empty_dest = &HashMap::<bitcoin::Address<address::NetworkUnchecked>, u64>::new();
         assert!(matches!(
             control.create_spend(empty_dest, &[confirmed_op_3], 5, None),
-            Err(CommandError::SpendCreation(
-                SpendCreationError::CoinSelection(..)
-            ))
+            Ok(CreateSpendResult::InsufficientFunds { .. }),
         ));
         // If we use a lower fee, the self-send will succeed.
-        let res = control
+        let psbt = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(empty_dest, &[confirmed_op_3], 1, None)
-            .unwrap();
-        let tx = res.psbt.unsigned_tx;
+            .unwrap()
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
+        let tx = psbt.unsigned_tx;
         let tx_prev_outpoints = tx
             .input
             .iter()
@@ -1792,20 +1842,32 @@ mod tests {
                 .iter()
                 .cloned()
                 .collect();
-        let mut psbt_a = control
+        let mut psbt_a = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(&destinations_a, &[dummy_op_a], 1, None)
             .unwrap()
-            .psbt;
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
         let txid_a = psbt_a.unsigned_tx.txid();
-        let psbt_b = control
+        let psbt_b = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(&destinations_b, &[dummy_op_b], 10, None)
             .unwrap()
-            .psbt;
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
         let txid_b = psbt_b.unsigned_tx.txid();
-        let psbt_c = control
+        let psbt_c = if let CreateSpendResult::Success { psbt, .. } = control
             .create_spend(&destinations_c, &[dummy_op_a, dummy_op_b], 100, None)
             .unwrap()
-            .psbt;
+        {
+            psbt
+        } else {
+            panic!("expect successful spend creation")
+        };
         let txid_c = psbt_c.unsigned_tx.txid();
 
         // We can store and query them all
diff --git a/src/database/mod.rs b/src/database/mod.rs
index 050581d5..9c2be7c6 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -46,9 +46,6 @@ pub trait DatabaseConnection {
     /// The network we are operating on.
     fn network(&mut self) -> bitcoin::Network;
 
-    /// The timestamp at wallet creation time
-    fn timestamp(&mut self) -> u32;
-
     /// Update our best chain seen.
     fn update_tip(&mut self, tip: &BlockChainTip);
 
@@ -164,10 +161,6 @@ impl DatabaseConnection for SqliteConn {
         self.db_tip().network
     }
 
-    fn timestamp(&mut self) -> u32 {
-        self.db_wallet().timestamp
-    }
-
     fn update_tip(&mut self, tip: &BlockChainTip) {
         self.update_tip(tip)
     }


Test patch:
diff --git a/src/testutils.rs b/src/testutils.rs
index 9034aaec..c175c156 100644
--- a/src/testutils.rs
+++ b/src/testutils.rs
@@ -5,13 +5,11 @@ use crate::{
     descriptors, DaemonHandle,
 };
 
-use std::convert::TryInto;
 use std::{
     collections::{HashMap, HashSet},
     env, fs, io, path, process,
     str::FromStr,
     sync, thread, time,
-    time::{SystemTime, UNIX_EPOCH},
 };
 
 use miniscript::{
@@ -131,7 +129,6 @@ struct DummyDbState {
     curr_tip: Option<BlockChainTip>,
     coins: HashMap<bitcoin::OutPoint, Coin>,
     spend_txs: HashMap<bitcoin::Txid, (Psbt, Option<u32>)>,
-    timestamp: u32,
 }
 
 pub struct DummyDatabase {
@@ -148,13 +145,6 @@ impl DatabaseInterface for DummyDatabase {
 
 impl DummyDatabase {
     pub fn new() -> DummyDatabase {
-        let now: u32 = SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .unwrap()
-            .as_secs()
-            .try_into()
-            .unwrap();
-
         DummyDatabase {
             db: sync::Arc::new(sync::RwLock::new(DummyDbState {
                 deposit_index: 0.into(),
@@ -162,7 +152,6 @@ impl DummyDatabase {
                 curr_tip: None,
                 coins: HashMap::new(),
                 spend_txs: HashMap::new(),
-                timestamp: now,
             })),
         }
     }
@@ -183,10 +172,6 @@ impl DatabaseConnection for DummyDatabase {
         self.db.read().unwrap().curr_tip
     }
 
-    fn timestamp(&mut self) -> u32 {
-        self.db.read().unwrap().timestamp
-    }
-
     fn update_tip(&mut self, tip: &BlockChainTip) {
         self.db.write().unwrap().curr_tip = Some(*tip);
     }
diff --git a/tests/test_rpc.py b/tests/test_rpc.py
index 18476c97..df70d2b2 100644
--- a/tests/test_rpc.py
+++ b/tests/test_rpc.py
@@ -22,7 +22,6 @@ from test_framework.utils import (
 
 def test_getinfo(lianad):
     res = lianad.rpc.getinfo()
-    assert 'timestamp' in res.keys()
     assert res["version"] == "4.0.0-dev"
     assert res["network"] == "regtest"
     wait_for(lambda: lianad.rpc.getinfo()["block_height"] == 101)
@@ -1152,6 +1151,49 @@ def test_rbfpsbt_bump_fee(lianad, bitcoind):
     )
 
 
+def test_rbfpsbt_insufficient_funds(lianad, bitcoind):
+    """Trying to increase the fee too much returns the missing funds amount."""
+    # Get a coin.
+    deposit_txid_1 = bitcoind.rpc.sendtoaddress(
+        lianad.rpc.getnewaddress()["address"], 30_000 / COIN
+    )
+    bitcoind.generate_block(1, wait_for_mempool=deposit_txid_1)
+    wait_for(lambda: len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 1)
+
+    # Create a spend that we will then attempt to replace.
+    destinations_1 = {
+        bitcoind.rpc.getnewaddress(): 29_800,
+    }
+    spend_res_1 = lianad.rpc.createspend(destinations_1, [], 1)
+    spend_psbt_1 = PSBT.from_base64(spend_res_1["psbt"])
+    spend_txid_1 = sign_and_broadcast_psbt(lianad, spend_psbt_1)
+
+    # We don't have sufficient funds to bump the fee.
+    assert "missing" in lianad.rpc.rbfpsbt(spend_txid_1, False, 2)
+    # We can still cancel it as the coin has enough value to create a single
+    # output at a higher feerate.
+    assert "psbt" in lianad.rpc.rbfpsbt(spend_txid_1, True)
+
+    wait_for(lambda: len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 0)
+    # Get another coin.
+    deposit_txid_2 = bitcoind.rpc.sendtoaddress(
+        lianad.rpc.getnewaddress()["address"], 5_200 / COIN
+    )
+    bitcoind.generate_block(1, wait_for_mempool=deposit_txid_2)
+    wait_for(lambda: len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 1)
+
+    # Create a spend that we will then attempt to cancel.
+    destinations_2 = {
+        bitcoind.rpc.getnewaddress(): 5_000,
+    }
+    spend_res_2 = lianad.rpc.createspend(destinations_2, [], 1)
+    spend_psbt_2 = PSBT.from_base64(spend_res_2["psbt"])
+    spend_txid_2 = sign_and_broadcast_psbt(lianad, spend_psbt_2)
+
+    # We don't have enough to create a transaction with feerate 2 sat/vb.
+    assert "missing" in lianad.rpc.rbfpsbt(spend_txid_2, True)
+
+
 def test_rbfpsbt_cancel(lianad, bitcoind):
     """Test the use of RBF to cancel a transaction."""
 
diff --git a/tests/test_spend.py b/tests/test_spend.py
index 82ac0f58..4ae75229 100644
--- a/tests/test_spend.py
+++ b/tests/test_spend.py
@@ -221,12 +221,8 @@ def test_send_to_self(lianad, bitcoind):
     spend_psbt = PSBT.from_base64(res["psbt"])
     assert len(spend_psbt.o) == len(spend_psbt.tx.vout) == 1
 
-    # Note they may ask for an impossible send-to-self. In this case we'll error cleanly.
-    with pytest.raises(
-        RpcError,
-        match="Insufficient funds. Missing \\d+ sats",
-    ):
-        lianad.rpc.createspend({}, outpoints, 40500)
+    # Note they may ask for an impossible send-to-self. In this case we'll report missing amount.
+    assert "missing" in lianad.rpc.createspend({}, outpoints, 40500)
 
     # Sign and broadcast the send-to-self transaction created above.
     signed_psbt = lianad.signer.sign_psbt(spend_psbt)
@@ -256,11 +252,7 @@ def test_coin_selection(lianad, bitcoind):
     dest_100_000 = {bitcoind.rpc.getnewaddress(): 100_000}
     # Coin selection is not possible if we have no coins.
     assert len(lianad.rpc.listcoins()["coins"]) == 0
-    with pytest.raises(
-        RpcError,
-        match="Coin selection error: 'Insufficient funds. Missing \\d+ sats.'",
-    ):
-        lianad.rpc.createspend(dest_100_000, [], 2)
+    assert "missing" in lianad.rpc.createspend(dest_100_000, [], 2)
 
     # Receive a coin in an unconfirmed deposit transaction.
     recv_addr = lianad.rpc.getnewaddress()["address"]
@@ -269,22 +261,14 @@ def test_coin_selection(lianad, bitcoind):
     # There are still no confirmed coins to use as candidates for selection.
     assert len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 0
     assert len(lianad.rpc.listcoins(["unconfirmed"])["coins"]) == 1
-    with pytest.raises(
-        RpcError,
-        match="Coin selection error: 'Insufficient funds. Missing \\d+ sats.'",
-    ):
-        lianad.rpc.createspend(dest_100_000, [], 2)
+    assert "missing" in lianad.rpc.createspend(dest_100_000, [], 2)
 
     # Confirm coin.
     bitcoind.generate_block(1, wait_for_mempool=deposit)
     wait_for(lambda: len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 1)
 
     # Insufficient funds for coin selection.
-    with pytest.raises(
-        RpcError,
-        match="Coin selection error: 'Insufficient funds. Missing \\d+ sats.'",
-    ):
-        lianad.rpc.createspend(dest_100_000, [], 2)
+    assert "missing" in lianad.rpc.createspend(dest_100_000, [], 2)
 
     # Reduce spend amount.
     dest_30_000 = {bitcoind.rpc.getnewaddress(): 30_000}
@@ -308,22 +292,14 @@ def test_coin_selection(lianad, bitcoind):
     assert len(lianad.rpc.listcoins(["unconfirmed"])["coins"]) == 1
     assert len(lianad.rpc.listcoins(["spending"])["coins"]) == 1
     # Check we cannot use coins as candidates if they are spending/spent or unconfirmed.
-    with pytest.raises(
-        RpcError,
-        match="Coin selection error: 'Insufficient funds. Missing \\d+ sats.'",
-    ):
-        lianad.rpc.createspend(dest_30_000, [], 2)
+    assert "missing" in lianad.rpc.createspend(dest_30_000, [], 2)
 
     # Now confirm the Spend.
     bitcoind.generate_block(1, wait_for_mempool=spend_txid)
     wait_for(lambda: len(lianad.rpc.listcoins(["confirmed"])["coins"]) == 1)
     # But its value is not enough for this Spend.
     dest_60_000 = {bitcoind.rpc.getnewaddress(): 60_000}
-    with pytest.raises(
-        RpcError,
-        match="Coin selection error: 'Insufficient funds. Missing \\d+ sats.'",
-    ):
-        lianad.rpc.createspend(dest_60_000, [], 2)
+    assert "missing" in lianad.rpc.createspend(dest_60_000, [], 2)
 
     # Get another coin to check coin selection with more than one candidate.
     recv_addr = lianad.rpc.getnewaddress()["address"]