From 328a0fecf2f697fd9ff2d79e83eada82fc2218ca Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 24 Sep 2026 15:42:41 +0300 Subject: [PATCH 1/9] feat: add USDT0 wallet support on Arbitrum --- .gitignore | 2 + Cargo.lock | 1185 +- Cargo.toml | 12 +- Package.swift | 22 +- README.md | 3 + bindings/android/gradle.properties | 2 +- bindings/ios/bitkitcore.swift | 24123 +++++++++------- bindings/ios/bitkitcoreFFI.h | 229 + src/lib.rs | 6 + src/modules/mod.rs | 1 + src/modules/usdt/README.md | 126 + src/modules/usdt/account.rs | 38 + src/modules/usdt/amount.rs | 75 + src/modules/usdt/deposits.rs | 383 + src/modules/usdt/errors.rs | 53 + .../usdt/fixtures/deposit-signature.json | 4 + .../usdt/fixtures/eip7702-vectors.json | 42 + src/modules/usdt/history.rs | 313 + src/modules/usdt/keys.rs | 68 + src/modules/usdt/mod.rs | 25 + src/modules/usdt/paymaster.rs | 387 + src/modules/usdt/payment_request.rs | 54 + src/modules/usdt/rpc.rs | 252 + src/modules/usdt/store.rs | 303 + src/modules/usdt/tests.rs | 1967 ++ src/modules/usdt/transaction.rs | 91 + src/modules/usdt/types.rs | 74 + src/modules/usdt/user_operation.rs | 179 + src/modules/usdt/wallet.rs | 664 + tests/usdt-fork/package-lock.json | 119 + tests/usdt-fork/package.json | 7 + tests/usdt-fork/provider.mjs | 216 + tests/usdt-fork/vectors.mjs | 122 + 33 files changed, 20000 insertions(+), 11147 deletions(-) create mode 100644 src/modules/usdt/README.md create mode 100644 src/modules/usdt/account.rs create mode 100644 src/modules/usdt/amount.rs create mode 100644 src/modules/usdt/deposits.rs create mode 100644 src/modules/usdt/errors.rs create mode 100644 src/modules/usdt/fixtures/deposit-signature.json create mode 100644 src/modules/usdt/fixtures/eip7702-vectors.json create mode 100644 src/modules/usdt/history.rs create mode 100644 src/modules/usdt/keys.rs create mode 100644 src/modules/usdt/mod.rs create mode 100644 src/modules/usdt/paymaster.rs create mode 100644 src/modules/usdt/payment_request.rs create mode 100644 src/modules/usdt/rpc.rs create mode 100644 src/modules/usdt/store.rs create mode 100644 src/modules/usdt/tests.rs create mode 100644 src/modules/usdt/transaction.rs create mode 100644 src/modules/usdt/types.rs create mode 100644 src/modules/usdt/user_operation.rs create mode 100644 src/modules/usdt/wallet.rs create mode 100644 tests/usdt-fork/package-lock.json create mode 100644 tests/usdt-fork/package.json create mode 100644 tests/usdt-fork/provider.mjs create mode 100644 tests/usdt-fork/vectors.mjs diff --git a/.gitignore b/.gitignore index 3b5da3e..94644d9 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ bindings/ios/BitkitCore.xcframework.zip .claude/worktrees *.local.* !*.local.template* + +/tests/usdt-fork/node_modules/ diff --git a/Cargo.lock b/Cargo.lock index 5366796..e3347fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "generic-array", ] @@ -75,6 +75,127 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-json-abi" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "858acd7fdad1e4a7057fd9c1b39c1f2cd6bcd57ebf3c56e2853c02ead049e816" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-primitives" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5e9dbd49258ac3ab893a481d46be29b58be7f734dcac46cd80b6b13ee36566c" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "fixed-cache", + "foldhash 0.2.0", + "hashbrown 0.17.1", + "indexmap 2.13.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.2", + "rapidhash", + "ruint", + "rustc-hash", + "secp256k1 0.31.1", + "serde", + "sha3 0.11.0", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24671b1f62edcf0f9b62994c7bf72cd621a04a4b99f5020ece1a647b40e2f103" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60dd79f578c3912f1fc2a150dbeb8110b8cfb976c60f98ebf6de9d5da5965a2b" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edb8520f2f94275e1caa73c85207dcc78ec402a9a4c429240f9a0783a8f16c0" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap 2.13.0", + "proc-macro-error3", + "proc-macro2", + "quote", + "sha3 0.11.0", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66af2d9344882172993be5f5cbfd349fdfa52cb548f7af8715b446fb35ef6001" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.117", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c2a8abc3334044013b23d5e438cc5dc802e5544cc42b713d0840d5185967cc" +dependencies = [ + "serde", + "winnow 1.0.4", +] + +[[package]] +name = "alloy-sol-types" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e6f87cf007caa54e95455875fbd25879b008d0d9c7c340a2c258b3a3400a28c" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "serde", +] + [[package]] name = "android_log-sys" version = "0.3.2" @@ -169,6 +290,269 @@ dependencies = [ "password-hash", ] +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a806ac6c8307b929df4645776290a50ee2aac754ad09d8bdf73391309e43af" +dependencies = [ + "ark-ff-asm 0.6.0", + "ark-ff-macros 0.6.0", + "ark-serialize 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "educe", + "num-bigint", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-ff-asm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1479009684adc073dff49a1025d3a7065b317a9ead25aaaca38cdc70058ba8a2" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-ff-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0691ed21ef00ef89c1e9bda832eba493dda3ec2f8d892fb25b705f73f06bb8" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74dd304fd536fb95d0a328e72be759209cc496a9da094c5bc56e5fea4f9e86b" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.6.0", + "digest 0.10.7", + "num-bigint", + "serde_with", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f153690697a2b91e5e1251ff98411ee5371500a111a0fd317a70e588eb300f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "367c9c827ed431bff6868b7aa926e05b16eb46603cc8b6e768e4a5553fa1d155" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -220,7 +604,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -262,6 +646,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -429,6 +824,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "unicode-normalization", + "zeroize", ] [[package]] @@ -441,7 +837,7 @@ dependencies = [ "base85", "bip39", "bitcoin 0.32.8", - "sha3", + "sha3 0.10.9", ] [[package]] @@ -589,8 +985,11 @@ dependencies = [ [[package]] name = "bitkitcore" -version = "0.5.18" +version = "0.6.0" dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", "android_logger", "async-trait", "base64 0.22.1", @@ -655,7 +1054,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -681,6 +1080,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block-padding" version = "0.3.3" @@ -710,7 +1118,7 @@ dependencies = [ "dbus", "dbus-tokio", "futures", - "itertools", + "itertools 0.14.0", "log", "serde", "serde-xml-rs", @@ -802,6 +1210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ "sha2", + "tinyvec", ] [[package]] @@ -838,6 +1247,12 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + [[package]] name = "bytecheck" version = "0.6.12" @@ -871,6 +1286,9 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] [[package]] name = "camino" @@ -898,7 +1316,7 @@ checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.27", "serde", "serde_json", "thiserror 2.0.18", @@ -1001,7 +1419,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "inout", "zeroize", ] @@ -1079,18 +1497,60 @@ dependencies = [ "memchr", ] +[[package]] +name = "const-hex" +version = "1.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e59eef12462b0f9b0a3620219be5d639afd79fe39dff0a42c3997061f9298b4" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -1261,6 +1721,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "crypto_secretbox" version = "0.1.1" @@ -1294,9 +1763,9 @@ dependencies = [ "cfg-if", "cpufeatures 0.2.17", "curve25519-dalek-derive", - "digest", + "digest 0.10.7", "fiat-crypto", - "rustc_version", + "rustc_version 0.4.1", "subtle", "zeroize", ] @@ -1418,6 +1887,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.117", + "unicode-xid", ] [[package]] @@ -1426,18 +1930,37 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1492,10 +2015,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -1523,6 +2047,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "either" version = "1.15.0" @@ -1586,10 +2122,11 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", + "pkcs8", "rand_core 0.6.4", "sec1", "subtle", @@ -1617,6 +2154,26 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "enum-ordinalize" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "env_filter" version = "0.1.4" @@ -1684,6 +2241,28 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -1706,6 +2285,28 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixed-cache" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe63500644ef0269fe6b744e7e5dc5c20b5eebf3d881bc2be53f194636f6583" +dependencies = [ + "equivalent", + "rapidhash", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1739,6 +2340,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1944,7 +2551,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows-link 0.1.3", + "windows-link 0.2.1", "windows-result 0.4.1", ] @@ -2100,7 +2707,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -2109,6 +2716,17 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", + "serde", + "serde_core", +] + [[package]] name = "hashlink" version = "0.9.1" @@ -2126,7 +2744,7 @@ checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", - "rustc_version", + "rustc_version 0.4.1", "serde", "spin 0.9.8", "stable_deref_trait", @@ -2208,7 +2826,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -2256,6 +2874,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.8.1" @@ -2466,6 +3093,37 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -2529,6 +3187,24 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -2575,10 +3251,12 @@ checksum = "34f877a98676d2fb664698d74cc6a51ce6c484ce8c770f05d0108ec9090aeb46" dependencies = [ "defmt", "jiff-static", + "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", + "windows-link 0.2.1", ] [[package]] @@ -2592,6 +3270,21 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.19.0" @@ -2656,6 +3349,7 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2", ] @@ -2668,6 +3362,41 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keccak" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + +[[package]] +name = "keccak-asm" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5dc2c0d691cbf7595cde551ced329cca99c2387c2cbc97754c5d0cd045d3ee" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy-regex" version = "3.6.0" @@ -2915,6 +3644,17 @@ dependencies = [ "libc", ] +[[package]] +name = "macro-string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "mainline" version = "5.4.0" @@ -3112,6 +3852,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3241,6 +3982,34 @@ dependencies = [ "winapi", ] +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "parking" version = "2.2.1" @@ -3306,13 +4075,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbkdf2" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", + "digest 0.10.7", "hmac", ] @@ -3322,6 +4097,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d3aca230fad2e6f6317ca0a72724338c4960cb97168a85cdee66df4a9a21a8" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.7.1" @@ -3329,7 +4114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.13.0", ] [[package]] @@ -3540,6 +4325,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -3559,6 +4355,16 @@ dependencies = [ "quote", ] +[[package]] +name = "proc-macro-error-attr3" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82366fd7d8b7a440d66d13418820c69df9b3908bcb1a0476d7f5ce5d12f5a04d" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro-error2" version = "2.0.1" @@ -3571,6 +4377,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "proc-macro-error3" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b511283ea8a74b4b39447b128c5d00f03a356b7424554b13e298a5550100d9ac" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3580,6 +4398,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bitflags 2.11.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "prost" version = "0.13.5" @@ -3597,7 +4430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -3617,7 +4450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3917,6 +4750,7 @@ checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", + "serde", ] [[package]] @@ -3966,6 +4800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", + "serde", ] [[package]] @@ -3974,6 +4809,15 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", +] + [[package]] name = "rand_xoshiro" version = "0.8.1" @@ -3983,6 +4827,15 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "rapidhash" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" +dependencies = [ + "rustversion", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -4001,6 +4854,26 @@ dependencies = [ "bitflags 2.11.0", ] +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "regex" version = "1.12.3" @@ -4113,7 +4986,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -4145,6 +5018,51 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2973657b5127d510e230f5c63d2d106af9c8f79393d8b9f4647323e8196bdde5" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "ark-ff 0.6.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rusb" version = "0.9.4" @@ -4206,13 +5124,28 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.27", ] [[package]] @@ -4368,6 +5301,30 @@ dependencies = [ "parking_lot 0.12.5", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -4431,6 +5388,7 @@ dependencies = [ "base16ct", "der", "generic-array", + "pkcs8", "subtle", "zeroize", ] @@ -4470,6 +5428,17 @@ dependencies = [ "secp256k1-sys 0.10.1", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes 0.14.1", + "rand 0.9.2", + "secp256k1-sys 0.11.0", +] + [[package]] name = "secp256k1" version = "0.32.0-beta.2" @@ -4498,6 +5467,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + [[package]] name = "secp256k1-sys" version = "0.12.0" @@ -4559,6 +5537,15 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.27" @@ -4569,6 +5556,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -4667,6 +5663,26 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "time", +] + [[package]] name = "serial_test" version = "3.4.0" @@ -4719,7 +5735,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] @@ -4736,7 +5752,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures 0.2.17", - "digest", + "digest 0.10.7", ] [[package]] @@ -4745,8 +5761,28 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ - "digest", - "keccak", + "digest 0.10.7", + "keccak 0.1.6", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.2", +] + +[[package]] +name = "sha3-asm" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6287fd675f713484342a89cbf0a386abef5f15919cfad607e5e1f19e1e15331" +dependencies = [ + "cc", + "cfg-if", ] [[package]] @@ -4780,7 +5816,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -4943,6 +5979,29 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6a2e3c7f7a3e4e83d1752cec5d1e357ced0cf96e85419b6a07f227db3def3a" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -5271,10 +6330,10 @@ version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ - "indexmap", + "indexmap 2.13.0", "toml_datetime", "toml_parser", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -5283,7 +6342,7 @@ version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ - "winnow", + "winnow 0.7.15", ] [[package]] @@ -5458,6 +6517,30 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unescaper" version = "0.1.10" @@ -5482,6 +6565,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -5518,7 +6607,7 @@ dependencies = [ "glob", "goblin", "heck", - "indexmap", + "indexmap 2.13.0", "once_cell", "serde", "tempfile", @@ -5537,6 +6626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98" dependencies = [ "anyhow", + "async-compat", "bytes", "once_cell", "static_assertions", @@ -5549,7 +6639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5592,7 +6682,7 @@ checksum = "dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "tempfile", "uniffi_internal_macros", ] @@ -5615,7 +6705,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "subtle", ] @@ -5836,7 +6926,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.13.0", "wasm-encoder", "wasmparser", ] @@ -5849,8 +6939,8 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", - "semver", + "indexmap 2.13.0", + "semver 1.0.27", ] [[package]] @@ -6204,6 +7294,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -6232,7 +7331,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.13.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -6263,7 +7362,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap", + "indexmap 2.13.0", "log", "serde", "serde_derive", @@ -6282,9 +7381,9 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.13.0", "log", - "semver", + "semver 1.0.27", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 2c97c1e..e02db8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "bitkitcore" -version = "0.5.18" +version = "0.6.0" edition = "2021" +rust-version = "1.90" [lib] crate-type = ["cdylib", "staticlib", "rlib"] @@ -9,7 +10,7 @@ name = "bitkitcore" path = "src/lib.rs" [dependencies] -uniffi = { version = "0.29.4", features = [ "cli", "bindgen" ] } +uniffi = { version = "0.29.4", features = [ "cli", "bindgen", "tokio" ] } serde_json = "1.0.114" serde = { version = "^1.0.209", features = ["derive"] } tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } @@ -35,7 +36,7 @@ r2d2_sqlite = "0.25.0" bitcoin-address-generator = "0.2.0" uuid = { version = "1.16.0", features = ["v4"] } hex = "0.4.3" -bip39 = "2.0" +bip39 = { version = "2.0", features = ["zeroize"] } bdk = { version = "0.30.2", features = ["all-keys"] } boltz-client = { version = "0.4.1", default-features = false, features = ["electrum", "ws"] } base64 = "0.22" @@ -48,6 +49,9 @@ pubky-app-specs = "0.4" # Bluetooth (for Android JNI initialization) # btleplug is also pulled in transitively by trezor-connect-rs btleplug = "0.11" +alloy-primitives = { version = "1", default-features = false, features = ["std", "serde"] } +alloy-sol-types = "1" +alloy-rlp = "0.3" # Trezor connect library - non-iOS platforms get USB + Bluetooth [target.'cfg(not(target_os = "ios"))'.dependencies] @@ -73,7 +77,7 @@ jade-client-rs = { git = "https://github.com/synonymdev/jade-client-rs", rev = " jade-client-rs = { git = "https://github.com/synonymdev/jade-client-rs", rev = "d46d02a", features = ["reqwest-pinserver", "serial"] } [dev-dependencies] -tokio = { version = "1.40.0", features = ["full"] } +tokio = { version = "1.40.0", features = ["full", "test-util"] } serde_json = "1.0.114" hex = "0.4.3" test-case = "3.3.1" diff --git a/Package.swift b/Package.swift index 3a5ef3a..3917d9e 100644 --- a/Package.swift +++ b/Package.swift @@ -2,11 +2,14 @@ // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription +import Foundation -let tag = "v0.5.18" -let checksum = "c2bae82e20dd485cc82285cff7667d487d0c64bbd6f4686cb4e8f599e2f6a1eb" +let tag = "v0.6.0" +let checksum = "66eefed1d64a6b4804eb8d84e9374cedf6d66fcd0c51728d3a35ceb55d29699d" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" +let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" + let package = Package( name: "bitkitcore", platforms: [ @@ -26,10 +29,15 @@ let package = Package( path: "./bindings/ios", sources: ["bitkitcore.swift"] ), - .binaryTarget( - name: "BitkitCoreFFI", - url: url, - checksum: checksum - ) + localBinary + ? .binaryTarget( + name: "BitkitCoreFFI", + path: "./dist/ios/BitkitCore.xcframework" + ) + : .binaryTarget( + name: "BitkitCoreFFI", + url: url, + checksum: checksum + ) ] ) diff --git a/README.md b/README.md index 790e807..f7093b6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ - Channel opening and management - Just-in-time channel creation - Gift code payments and redemption +- [USDT Module](src/modules/usdt/README.md) + - Seed-derived Arbitrum EIP-7702 accounts and USDT-only payment fees + - Quotes, signing, payment recovery and persistent activity - Trezor Module - Integration with Trezor hardware wallets through deep linking - Get device features and capabilities diff --git a/bindings/android/gradle.properties b/bindings/android/gradle.properties index 402d8e0..750a009 100644 --- a/bindings/android/gradle.properties +++ b/bindings/android/gradle.properties @@ -3,4 +3,4 @@ android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official group=com.synonym -version=0.5.18 +version=0.6.0 diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index 27fbb8c..d01e577 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -2561,88 +2561,2317 @@ public func FfiConverterTypeUrDecoder_lower(_ value: UrDecoder) -> UnsafeMutable + + +public protocol UsdtDepositClientProtocol: AnyObject, Sendable { + + func detail(depositId: String, offset: UInt32, mnemonic: String, passphrase: String?) async throws -> UsdtDepositDetail + + func history(offset: UInt32, mnemonic: String, passphrase: String?) async throws -> UsdtDepositPage + + func networks() async throws -> [UsdtDepositNetwork] + + func receive(network: UsdtDepositNetwork, amount: UInt64, mnemonic: String, passphrase: String?) async throws -> UsdtDepositAddress + + func requestRefund(depositId: String, offset: UInt32, refundAddress: String, network: UsdtDepositNetwork, mnemonic: String, passphrase: String?) async throws + +} +open class UsdtDepositClient: UsdtDepositClientProtocol, @unchecked Sendable { + fileprivate let pointer: UnsafeMutableRawPointer! + + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public struct NoPointer { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + // This constructor can be used to instantiate a fake object. + // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public init(noPointer: NoPointer) { + self.pointer = nil + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public func uniffiClonePointer() -> UnsafeMutableRawPointer { + return try! rustCall { uniffi_bitkitcore_fn_clone_usdtdepositclient(self.pointer, $0) } + } +public convenience init(address: String, serviceUrl: String)throws { + let pointer = + try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_constructor_usdtdepositclient_new( + FfiConverterString.lower(address), + FfiConverterString.lower(serviceUrl),$0 + ) +} + self.init(unsafeFromRawPointer: pointer) +} + + deinit { + guard let pointer = pointer else { + return + } + + try! rustCall { uniffi_bitkitcore_fn_free_usdtdepositclient(pointer, $0) } + } + + + + +open func detail(depositId: String, offset: UInt32, mnemonic: String, passphrase: String?)async throws -> UsdtDepositDetail { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtdepositclient_detail( + self.uniffiClonePointer(), + FfiConverterString.lower(depositId),FfiConverterUInt32.lower(offset),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeUsdtDepositDetail_lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func history(offset: UInt32, mnemonic: String, passphrase: String?)async throws -> UsdtDepositPage { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtdepositclient_history( + self.uniffiClonePointer(), + FfiConverterUInt32.lower(offset),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeUsdtDepositPage_lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func networks()async throws -> [UsdtDepositNetwork] { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtdepositclient_networks( + self.uniffiClonePointer() + + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterSequenceTypeUsdtDepositNetwork.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func receive(network: UsdtDepositNetwork, amount: UInt64, mnemonic: String, passphrase: String?)async throws -> UsdtDepositAddress { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtdepositclient_receive( + self.uniffiClonePointer(), + FfiConverterTypeUsdtDepositNetwork_lower(network),FfiConverterUInt64.lower(amount),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeUsdtDepositAddress_lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func requestRefund(depositId: String, offset: UInt32, refundAddress: String, network: UsdtDepositNetwork, mnemonic: String, passphrase: String?)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtdepositclient_request_refund( + self.uniffiClonePointer(), + FfiConverterString.lower(depositId),FfiConverterUInt32.lower(offset),FfiConverterString.lower(refundAddress),FfiConverterTypeUsdtDepositNetwork_lower(network),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_void, + completeFunc: ffi_bitkitcore_rust_future_complete_void, + freeFunc: ffi_bitkitcore_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeUsdtDepositClient: FfiConverter { + + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = UsdtDepositClient + + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtDepositClient { + return UsdtDepositClient(unsafeFromRawPointer: pointer) + } + + public static func lower(_ value: UsdtDepositClient) -> UnsafeMutableRawPointer { + return value.uniffiClonePointer() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositClient { + let v: UInt64 = try readInt(&buf) + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + public static func write(_ value: UsdtDepositClient, into buf: inout [UInt8]) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUsdtDepositClient_lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtDepositClient { + return try FfiConverterTypeUsdtDepositClient.lift(pointer) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUsdtDepositClient_lower(_ value: UsdtDepositClient) -> UnsafeMutableRawPointer { + return FfiConverterTypeUsdtDepositClient.lower(value) +} + + + + + + +public protocol UsdtWalletProtocol: AnyObject, Sendable { + + func balance() async throws -> UInt64 + + func history() throws -> [UsdtTransfer] + + func quoteTransfer(recipient: String, amount: UInt64, destination: UsdtDestination) async throws -> UsdtQuote + + func receiveAddress() -> String + + func receiveUri() -> String + + func refreshTransfers() async throws -> [UsdtTransfer] + + func send(quoteId: String, mnemonic: String, passphrase: String?) async throws -> UsdtTransfer + + func syncHistory() async throws -> Bool + +} +open class UsdtWallet: UsdtWalletProtocol, @unchecked Sendable { + fileprivate let pointer: UnsafeMutableRawPointer! + + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public struct NoPointer { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + // This constructor can be used to instantiate a fake object. + // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public init(noPointer: NoPointer) { + self.pointer = nil + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public func uniffiClonePointer() -> UnsafeMutableRawPointer { + return try! rustCall { uniffi_bitkitcore_fn_clone_usdtwallet(self.pointer, $0) } + } +public convenience init(address: String, storagePath: String, rpcUrl: String, bundlerUrl: String)throws { + let pointer = + try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_constructor_usdtwallet_new( + FfiConverterString.lower(address), + FfiConverterString.lower(storagePath), + FfiConverterString.lower(rpcUrl), + FfiConverterString.lower(bundlerUrl),$0 + ) +} + self.init(unsafeFromRawPointer: pointer) +} + + deinit { + guard let pointer = pointer else { + return + } + + try! rustCall { uniffi_bitkitcore_fn_free_usdtwallet(pointer, $0) } + } + + + + +open func balance()async throws -> UInt64 { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_balance( + self.uniffiClonePointer() + + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_u64, + completeFunc: ffi_bitkitcore_rust_future_complete_u64, + freeFunc: ffi_bitkitcore_rust_future_free_u64, + liftFunc: FfiConverterUInt64.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func history()throws -> [UsdtTransfer] { + return try FfiConverterSequenceTypeUsdtTransfer.lift(try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_method_usdtwallet_history(self.uniffiClonePointer(),$0 + ) +}) +} + +open func quoteTransfer(recipient: String, amount: UInt64, destination: UsdtDestination)async throws -> UsdtQuote { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_quote_transfer( + self.uniffiClonePointer(), + FfiConverterString.lower(recipient),FfiConverterUInt64.lower(amount),FfiConverterTypeUsdtDestination_lower(destination) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeUsdtQuote_lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func receiveAddress() -> String { + return try! FfiConverterString.lift(try! rustCall() { + uniffi_bitkitcore_fn_method_usdtwallet_receive_address(self.uniffiClonePointer(),$0 + ) +}) +} + +open func receiveUri() -> String { + return try! FfiConverterString.lift(try! rustCall() { + uniffi_bitkitcore_fn_method_usdtwallet_receive_uri(self.uniffiClonePointer(),$0 + ) +}) +} + +open func refreshTransfers()async throws -> [UsdtTransfer] { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfers( + self.uniffiClonePointer() + + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterSequenceTypeUsdtTransfer.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func send(quoteId: String, mnemonic: String, passphrase: String?)async throws -> UsdtTransfer { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_send( + self.uniffiClonePointer(), + FfiConverterString.lower(quoteId),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeUsdtTransfer_lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + +open func syncHistory()async throws -> Bool { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_sync_history( + self.uniffiClonePointer() + + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_i8, + completeFunc: ffi_bitkitcore_rust_future_complete_i8, + freeFunc: ffi_bitkitcore_rust_future_free_i8, + liftFunc: FfiConverterBool.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeUsdtWallet: FfiConverter { + + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = UsdtWallet + + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtWallet { + return UsdtWallet(unsafeFromRawPointer: pointer) + } + + public static func lower(_ value: UsdtWallet) -> UnsafeMutableRawPointer { + return value.uniffiClonePointer() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtWallet { + let v: UInt64 = try readInt(&buf) + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + public static func write(_ value: UsdtWallet, into buf: inout [UInt8]) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUsdtWallet_lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtWallet { + return try FfiConverterTypeUsdtWallet.lift(pointer) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeUsdtWallet_lower(_ value: UsdtWallet) -> UnsafeMutableRawPointer { + return FfiConverterTypeUsdtWallet.lower(value) +} + + + + +/** + * Grouped address lists for an account. + */ +public struct AccountAddresses { + /** + * Used receive addresses (have at least one transaction) + */ + public var used: [AddressInfo] + /** + * Unused receive addresses (no transactions yet) + */ + public var unused: [AddressInfo] + /** + * Change addresses + */ + public var change: [AddressInfo] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Used receive addresses (have at least one transaction) + */used: [AddressInfo], + /** + * Unused receive addresses (no transactions yet) + */unused: [AddressInfo], + /** + * Change addresses + */change: [AddressInfo]) { + self.used = used + self.unused = unused + self.change = change + } +} + +#if compiler(>=6) +extension AccountAddresses: Sendable {} +#endif + + +extension AccountAddresses: Equatable, Hashable { + public static func ==(lhs: AccountAddresses, rhs: AccountAddresses) -> Bool { + if lhs.used != rhs.used { + return false + } + if lhs.unused != rhs.unused { + return false + } + if lhs.change != rhs.change { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(used) + hasher.combine(unused) + hasher.combine(change) + } +} + +extension AccountAddresses: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAccountAddresses: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountAddresses { + return + try AccountAddresses( + used: FfiConverterSequenceTypeAddressInfo.read(from: &buf), + unused: FfiConverterSequenceTypeAddressInfo.read(from: &buf), + change: FfiConverterSequenceTypeAddressInfo.read(from: &buf) + ) + } + + public static func write(_ value: AccountAddresses, into buf: inout [UInt8]) { + FfiConverterSequenceTypeAddressInfo.write(value.used, into: &buf) + FfiConverterSequenceTypeAddressInfo.write(value.unused, into: &buf) + FfiConverterSequenceTypeAddressInfo.write(value.change, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountAddresses_lift(_ buf: RustBuffer) throws -> AccountAddresses { + return try FfiConverterTypeAccountAddresses.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountAddresses_lower(_ value: AccountAddresses) -> RustBuffer { + return FfiConverterTypeAccountAddresses.lower(value) +} + + +/** + * Result from querying an extended public key via Electrum. + */ +public struct AccountInfoResult { + /** + * The account structure with addresses and UTXOs + */ + public var account: ComposeAccount + /** + * Total confirmed balance in satoshis + */ + public var balance: UInt64 + /** + * Number of UTXOs + */ + public var utxoCount: UInt32 + /** + * The detected or specified account type + */ + public var accountType: AccountType + /** + * The current blockchain tip height + */ + public var blockHeight: UInt32 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * The account structure with addresses and UTXOs + */account: ComposeAccount, + /** + * Total confirmed balance in satoshis + */balance: UInt64, + /** + * Number of UTXOs + */utxoCount: UInt32, + /** + * The detected or specified account type + */accountType: AccountType, + /** + * The current blockchain tip height + */blockHeight: UInt32) { + self.account = account + self.balance = balance + self.utxoCount = utxoCount + self.accountType = accountType + self.blockHeight = blockHeight + } +} + +#if compiler(>=6) +extension AccountInfoResult: Sendable {} +#endif + + +extension AccountInfoResult: Equatable, Hashable { + public static func ==(lhs: AccountInfoResult, rhs: AccountInfoResult) -> Bool { + if lhs.account != rhs.account { + return false + } + if lhs.balance != rhs.balance { + return false + } + if lhs.utxoCount != rhs.utxoCount { + return false + } + if lhs.accountType != rhs.accountType { + return false + } + if lhs.blockHeight != rhs.blockHeight { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(account) + hasher.combine(balance) + hasher.combine(utxoCount) + hasher.combine(accountType) + hasher.combine(blockHeight) + } +} + +extension AccountInfoResult: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAccountInfoResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoResult { + return + try AccountInfoResult( + account: FfiConverterTypeComposeAccount.read(from: &buf), + balance: FfiConverterUInt64.read(from: &buf), + utxoCount: FfiConverterUInt32.read(from: &buf), + accountType: FfiConverterTypeAccountType.read(from: &buf), + blockHeight: FfiConverterUInt32.read(from: &buf) + ) + } + + public static func write(_ value: AccountInfoResult, into buf: inout [UInt8]) { + FfiConverterTypeComposeAccount.write(value.account, into: &buf) + FfiConverterUInt64.write(value.balance, into: &buf) + FfiConverterUInt32.write(value.utxoCount, into: &buf) + FfiConverterTypeAccountType.write(value.accountType, into: &buf) + FfiConverterUInt32.write(value.blockHeight, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountInfoResult_lift(_ buf: RustBuffer) throws -> AccountInfoResult { + return try FfiConverterTypeAccountInfoResult.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountInfoResult_lower(_ value: AccountInfoResult) -> RustBuffer { + return FfiConverterTypeAccountInfoResult.lower(value) +} + + +/** + * A UTXO associated with an account or address. + */ +public struct AccountUtxo { + /** + * Transaction ID (hex) + */ + public var txid: String + /** + * Output index + */ + public var vout: UInt32 + /** + * Amount in satoshis + */ + public var amount: UInt64 + /** + * Block height where the UTXO was confirmed (0 if unconfirmed) + */ + public var blockHeight: UInt32 + /** + * Address holding this UTXO + */ + public var address: String + /** + * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") + */ + public var path: String + /** + * Number of confirmations (0 if unconfirmed) + */ + public var confirmations: UInt32 + /** + * Whether this is a coinbase output + */ + public var coinbase: Bool + /** + * Whether this UTXO is owned by the account + */ + public var own: Bool + /** + * Whether this UTXO must be included in the transaction + */ + public var required: Bool? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Transaction ID (hex) + */txid: String, + /** + * Output index + */vout: UInt32, + /** + * Amount in satoshis + */amount: UInt64, + /** + * Block height where the UTXO was confirmed (0 if unconfirmed) + */blockHeight: UInt32, + /** + * Address holding this UTXO + */address: String, + /** + * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") + */path: String, + /** + * Number of confirmations (0 if unconfirmed) + */confirmations: UInt32, + /** + * Whether this is a coinbase output + */coinbase: Bool, + /** + * Whether this UTXO is owned by the account + */own: Bool, + /** + * Whether this UTXO must be included in the transaction + */required: Bool?) { + self.txid = txid + self.vout = vout + self.amount = amount + self.blockHeight = blockHeight + self.address = address + self.path = path + self.confirmations = confirmations + self.coinbase = coinbase + self.own = own + self.required = required + } +} + +#if compiler(>=6) +extension AccountUtxo: Sendable {} +#endif + + +extension AccountUtxo: Equatable, Hashable { + public static func ==(lhs: AccountUtxo, rhs: AccountUtxo) -> Bool { + if lhs.txid != rhs.txid { + return false + } + if lhs.vout != rhs.vout { + return false + } + if lhs.amount != rhs.amount { + return false + } + if lhs.blockHeight != rhs.blockHeight { + return false + } + if lhs.address != rhs.address { + return false + } + if lhs.path != rhs.path { + return false + } + if lhs.confirmations != rhs.confirmations { + return false + } + if lhs.coinbase != rhs.coinbase { + return false + } + if lhs.own != rhs.own { + return false + } + if lhs.required != rhs.required { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(txid) + hasher.combine(vout) + hasher.combine(amount) + hasher.combine(blockHeight) + hasher.combine(address) + hasher.combine(path) + hasher.combine(confirmations) + hasher.combine(coinbase) + hasher.combine(own) + hasher.combine(required) + } +} + +extension AccountUtxo: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAccountUtxo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountUtxo { + return + try AccountUtxo( + txid: FfiConverterString.read(from: &buf), + vout: FfiConverterUInt32.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + blockHeight: FfiConverterUInt32.read(from: &buf), + address: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + confirmations: FfiConverterUInt32.read(from: &buf), + coinbase: FfiConverterBool.read(from: &buf), + own: FfiConverterBool.read(from: &buf), + required: FfiConverterOptionBool.read(from: &buf) + ) + } + + public static func write(_ value: AccountUtxo, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt32.write(value.vout, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterUInt32.write(value.blockHeight, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterUInt32.write(value.confirmations, into: &buf) + FfiConverterBool.write(value.coinbase, into: &buf) + FfiConverterBool.write(value.own, into: &buf) + FfiConverterOptionBool.write(value.required, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountUtxo_lift(_ buf: RustBuffer) throws -> AccountUtxo { + return try FfiConverterTypeAccountUtxo.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountUtxo_lower(_ value: AccountUtxo) -> RustBuffer { + return FfiConverterTypeAccountUtxo.lower(value) +} + + +public struct ActivityTags { + public var walletId: String + public var activityId: String + public var tags: [String] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(walletId: String, activityId: String, tags: [String]) { + self.walletId = walletId + self.activityId = activityId + self.tags = tags + } +} + +#if compiler(>=6) +extension ActivityTags: Sendable {} +#endif + + +extension ActivityTags: Equatable, Hashable { + public static func ==(lhs: ActivityTags, rhs: ActivityTags) -> Bool { + if lhs.walletId != rhs.walletId { + return false + } + if lhs.activityId != rhs.activityId { + return false + } + if lhs.tags != rhs.tags { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(walletId) + hasher.combine(activityId) + hasher.combine(tags) + } +} + +extension ActivityTags: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeActivityTags: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityTags { + return + try ActivityTags( + walletId: FfiConverterString.read(from: &buf), + activityId: FfiConverterString.read(from: &buf), + tags: FfiConverterSequenceString.read(from: &buf) + ) + } + + public static func write(_ value: ActivityTags, into buf: inout [UInt8]) { + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.activityId, into: &buf) + FfiConverterSequenceString.write(value.tags, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityTags_lift(_ buf: RustBuffer) throws -> ActivityTags { + return try FfiConverterTypeActivityTags.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityTags_lower(_ value: ActivityTags) -> RustBuffer { + return FfiConverterTypeActivityTags.lower(value) +} + + +/** + * Information about a single address in an account. + */ +public struct AddressInfo { + /** + * The Bitcoin address + */ + public var address: String + /** + * BIP32 derivation path + */ + public var path: String + /** + * Number of transfers (real count in `get_address_info`, 1/0 presence flag in `get_account_info`) + */ + public var transfers: UInt32 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * The Bitcoin address + */address: String, + /** + * BIP32 derivation path + */path: String, + /** + * Number of transfers (real count in `get_address_info`, 1/0 presence flag in `get_account_info`) + */transfers: UInt32) { + self.address = address + self.path = path + self.transfers = transfers + } +} + +#if compiler(>=6) +extension AddressInfo: Sendable {} +#endif + + +extension AddressInfo: Equatable, Hashable { + public static func ==(lhs: AddressInfo, rhs: AddressInfo) -> Bool { + if lhs.address != rhs.address { + return false + } + if lhs.path != rhs.path { + return false + } + if lhs.transfers != rhs.transfers { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(address) + hasher.combine(path) + hasher.combine(transfers) + } +} + +extension AddressInfo: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAddressInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressInfo { + return + try AddressInfo( + address: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + transfers: FfiConverterUInt32.read(from: &buf) + ) + } + + public static func write(_ value: AddressInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterUInt32.write(value.transfers, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressInfo_lift(_ buf: RustBuffer) throws -> AddressInfo { + return try FfiConverterTypeAddressInfo.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressInfo_lower(_ value: AddressInfo) -> RustBuffer { + return FfiConverterTypeAddressInfo.lower(value) +} + + +/** + * Fees and limits for a swap pair, used to size a swap and present costs. + */ +public struct BoltzPairInfo { + /** + * Pair hash identifying the current terms (passed back to Boltz if needed). + */ + public var hash: String + /** + * Exchange rate of the pair. + */ + public var rate: Double + /** + * Minimum swap amount in satoshis. + */ + public var minimalSat: UInt64 + /** + * Maximum swap amount in satoshis. + */ + public var maximalSat: UInt64 + /** + * Boltz service fee as a percentage of the swap amount. + */ + public var feePercentage: Double + /** + * Estimated absolute miner fees in satoshis. + */ + public var minerFeesSat: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Pair hash identifying the current terms (passed back to Boltz if needed). + */hash: String, + /** + * Exchange rate of the pair. + */rate: Double, + /** + * Minimum swap amount in satoshis. + */minimalSat: UInt64, + /** + * Maximum swap amount in satoshis. + */maximalSat: UInt64, + /** + * Boltz service fee as a percentage of the swap amount. + */feePercentage: Double, + /** + * Estimated absolute miner fees in satoshis. + */minerFeesSat: UInt64) { + self.hash = hash + self.rate = rate + self.minimalSat = minimalSat + self.maximalSat = maximalSat + self.feePercentage = feePercentage + self.minerFeesSat = minerFeesSat + } +} + +#if compiler(>=6) +extension BoltzPairInfo: Sendable {} +#endif + + +extension BoltzPairInfo: Equatable, Hashable { + public static func ==(lhs: BoltzPairInfo, rhs: BoltzPairInfo) -> Bool { + if lhs.hash != rhs.hash { + return false + } + if lhs.rate != rhs.rate { + return false + } + if lhs.minimalSat != rhs.minimalSat { + return false + } + if lhs.maximalSat != rhs.maximalSat { + return false + } + if lhs.feePercentage != rhs.feePercentage { + return false + } + if lhs.minerFeesSat != rhs.minerFeesSat { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(hash) + hasher.combine(rate) + hasher.combine(minimalSat) + hasher.combine(maximalSat) + hasher.combine(feePercentage) + hasher.combine(minerFeesSat) + } +} + +extension BoltzPairInfo: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBoltzPairInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzPairInfo { + return + try BoltzPairInfo( + hash: FfiConverterString.read(from: &buf), + rate: FfiConverterDouble.read(from: &buf), + minimalSat: FfiConverterUInt64.read(from: &buf), + maximalSat: FfiConverterUInt64.read(from: &buf), + feePercentage: FfiConverterDouble.read(from: &buf), + minerFeesSat: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: BoltzPairInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.hash, into: &buf) + FfiConverterDouble.write(value.rate, into: &buf) + FfiConverterUInt64.write(value.minimalSat, into: &buf) + FfiConverterUInt64.write(value.maximalSat, into: &buf) + FfiConverterDouble.write(value.feePercentage, into: &buf) + FfiConverterUInt64.write(value.minerFeesSat, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBoltzPairInfo_lift(_ buf: RustBuffer) throws -> BoltzPairInfo { + return try FfiConverterTypeBoltzPairInfo.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBoltzPairInfo_lower(_ value: BoltzPairInfo) -> RustBuffer { + return FfiConverterTypeBoltzPairInfo.lower(value) +} + + +/** + * A persisted swap and its current state, returned by the listing/query APIs. + */ +public struct BoltzSwap { + public var id: String + public var swapType: BoltzSwapType + public var status: BoltzSwapStatus + public var network: BoltzNetwork + /** + * Deterministic BIP85 index used to derive this swap's key and preimage + * from the wallet seed. The recovery handle: given the seed and this index + * (or by scanning indices), the swap's secrets can be reconstructed. + */ + public var swapIndex: UInt64 + /** + * For submarine swaps: the amount to lock onchain. For reverse swaps: the + * Lightning invoice amount. + */ + public var amountSat: UInt64 + /** + * For reverse swaps: the onchain amount that will be received. + */ + public var onchainAmountSat: UInt64? + /** + * Lightning invoice associated with the swap (the hold invoice for reverse + * swaps, the invoice Boltz pays for submarine swaps). + */ + public var invoice: String? + /** + * Onchain lockup address. + */ + public var lockupAddress: String? + /** + * The address funds are claimed to (reverse) or refunded to (submarine). + */ + public var onchainAddress: String? + public var timeoutBlockHeight: UInt64 + public var createdAt: UInt64 + /** + * Txid of the claim transaction once broadcast (reverse swaps). + */ + public var claimTxId: String? + /** + * Txid of the refund transaction once broadcast (submarine swaps). + */ + public var refundTxId: String? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(id: String, swapType: BoltzSwapType, status: BoltzSwapStatus, network: BoltzNetwork, + /** + * Deterministic BIP85 index used to derive this swap's key and preimage + * from the wallet seed. The recovery handle: given the seed and this index + * (or by scanning indices), the swap's secrets can be reconstructed. + */swapIndex: UInt64, + /** + * For submarine swaps: the amount to lock onchain. For reverse swaps: the + * Lightning invoice amount. + */amountSat: UInt64, + /** + * For reverse swaps: the onchain amount that will be received. + */onchainAmountSat: UInt64?, + /** + * Lightning invoice associated with the swap (the hold invoice for reverse + * swaps, the invoice Boltz pays for submarine swaps). + */invoice: String?, + /** + * Onchain lockup address. + */lockupAddress: String?, + /** + * The address funds are claimed to (reverse) or refunded to (submarine). + */onchainAddress: String?, timeoutBlockHeight: UInt64, createdAt: UInt64, + /** + * Txid of the claim transaction once broadcast (reverse swaps). + */claimTxId: String?, + /** + * Txid of the refund transaction once broadcast (submarine swaps). + */refundTxId: String?) { + self.id = id + self.swapType = swapType + self.status = status + self.network = network + self.swapIndex = swapIndex + self.amountSat = amountSat + self.onchainAmountSat = onchainAmountSat + self.invoice = invoice + self.lockupAddress = lockupAddress + self.onchainAddress = onchainAddress + self.timeoutBlockHeight = timeoutBlockHeight + self.createdAt = createdAt + self.claimTxId = claimTxId + self.refundTxId = refundTxId + } +} + +#if compiler(>=6) +extension BoltzSwap: Sendable {} +#endif + + +extension BoltzSwap: Equatable, Hashable { + public static func ==(lhs: BoltzSwap, rhs: BoltzSwap) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.swapType != rhs.swapType { + return false + } + if lhs.status != rhs.status { + return false + } + if lhs.network != rhs.network { + return false + } + if lhs.swapIndex != rhs.swapIndex { + return false + } + if lhs.amountSat != rhs.amountSat { + return false + } + if lhs.onchainAmountSat != rhs.onchainAmountSat { + return false + } + if lhs.invoice != rhs.invoice { + return false + } + if lhs.lockupAddress != rhs.lockupAddress { + return false + } + if lhs.onchainAddress != rhs.onchainAddress { + return false + } + if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { + return false + } + if lhs.createdAt != rhs.createdAt { + return false + } + if lhs.claimTxId != rhs.claimTxId { + return false + } + if lhs.refundTxId != rhs.refundTxId { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(swapType) + hasher.combine(status) + hasher.combine(network) + hasher.combine(swapIndex) + hasher.combine(amountSat) + hasher.combine(onchainAmountSat) + hasher.combine(invoice) + hasher.combine(lockupAddress) + hasher.combine(onchainAddress) + hasher.combine(timeoutBlockHeight) + hasher.combine(createdAt) + hasher.combine(claimTxId) + hasher.combine(refundTxId) + } +} + +extension BoltzSwap: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBoltzSwap: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwap { + return + try BoltzSwap( + id: FfiConverterString.read(from: &buf), + swapType: FfiConverterTypeBoltzSwapType.read(from: &buf), + status: FfiConverterTypeBoltzSwapStatus.read(from: &buf), + network: FfiConverterTypeBoltzNetwork.read(from: &buf), + swapIndex: FfiConverterUInt64.read(from: &buf), + amountSat: FfiConverterUInt64.read(from: &buf), + onchainAmountSat: FfiConverterOptionUInt64.read(from: &buf), + invoice: FfiConverterOptionString.read(from: &buf), + lockupAddress: FfiConverterOptionString.read(from: &buf), + onchainAddress: FfiConverterOptionString.read(from: &buf), + timeoutBlockHeight: FfiConverterUInt64.read(from: &buf), + createdAt: FfiConverterUInt64.read(from: &buf), + claimTxId: FfiConverterOptionString.read(from: &buf), + refundTxId: FfiConverterOptionString.read(from: &buf) + ) + } + + public static func write(_ value: BoltzSwap, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypeBoltzSwapType.write(value.swapType, into: &buf) + FfiConverterTypeBoltzSwapStatus.write(value.status, into: &buf) + FfiConverterTypeBoltzNetwork.write(value.network, into: &buf) + FfiConverterUInt64.write(value.swapIndex, into: &buf) + FfiConverterUInt64.write(value.amountSat, into: &buf) + FfiConverterOptionUInt64.write(value.onchainAmountSat, into: &buf) + FfiConverterOptionString.write(value.invoice, into: &buf) + FfiConverterOptionString.write(value.lockupAddress, into: &buf) + FfiConverterOptionString.write(value.onchainAddress, into: &buf) + FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) + FfiConverterUInt64.write(value.createdAt, into: &buf) + FfiConverterOptionString.write(value.claimTxId, into: &buf) + FfiConverterOptionString.write(value.refundTxId, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBoltzSwap_lift(_ buf: RustBuffer) throws -> BoltzSwap { + return try FfiConverterTypeBoltzSwap.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBoltzSwap_lower(_ value: BoltzSwap) -> RustBuffer { + return FfiConverterTypeBoltzSwap.lower(value) +} + + +public struct ChannelLiquidityOptions { + public var defaultLspBalanceSat: UInt64 + public var minLspBalanceSat: UInt64 + public var maxLspBalanceSat: UInt64 + public var maxClientBalanceSat: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(defaultLspBalanceSat: UInt64, minLspBalanceSat: UInt64, maxLspBalanceSat: UInt64, maxClientBalanceSat: UInt64) { + self.defaultLspBalanceSat = defaultLspBalanceSat + self.minLspBalanceSat = minLspBalanceSat + self.maxLspBalanceSat = maxLspBalanceSat + self.maxClientBalanceSat = maxClientBalanceSat + } +} + +#if compiler(>=6) +extension ChannelLiquidityOptions: Sendable {} +#endif + + +extension ChannelLiquidityOptions: Equatable, Hashable { + public static func ==(lhs: ChannelLiquidityOptions, rhs: ChannelLiquidityOptions) -> Bool { + if lhs.defaultLspBalanceSat != rhs.defaultLspBalanceSat { + return false + } + if lhs.minLspBalanceSat != rhs.minLspBalanceSat { + return false + } + if lhs.maxLspBalanceSat != rhs.maxLspBalanceSat { + return false + } + if lhs.maxClientBalanceSat != rhs.maxClientBalanceSat { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(defaultLspBalanceSat) + hasher.combine(minLspBalanceSat) + hasher.combine(maxLspBalanceSat) + hasher.combine(maxClientBalanceSat) + } +} + +extension ChannelLiquidityOptions: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeChannelLiquidityOptions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ChannelLiquidityOptions { + return + try ChannelLiquidityOptions( + defaultLspBalanceSat: FfiConverterUInt64.read(from: &buf), + minLspBalanceSat: FfiConverterUInt64.read(from: &buf), + maxLspBalanceSat: FfiConverterUInt64.read(from: &buf), + maxClientBalanceSat: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: ChannelLiquidityOptions, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.defaultLspBalanceSat, into: &buf) + FfiConverterUInt64.write(value.minLspBalanceSat, into: &buf) + FfiConverterUInt64.write(value.maxLspBalanceSat, into: &buf) + FfiConverterUInt64.write(value.maxClientBalanceSat, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChannelLiquidityOptions_lift(_ buf: RustBuffer) throws -> ChannelLiquidityOptions { + return try FfiConverterTypeChannelLiquidityOptions.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChannelLiquidityOptions_lower(_ value: ChannelLiquidityOptions) -> RustBuffer { + return FfiConverterTypeChannelLiquidityOptions.lower(value) +} + + +public struct ChannelLiquidityParams { + public var clientBalanceSat: UInt64 + public var existingChannelsTotalSat: UInt64 + public var minChannelSizeSat: UInt64 + public var maxChannelSizeSat: UInt64 + public var satsPerEur: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(clientBalanceSat: UInt64, existingChannelsTotalSat: UInt64, minChannelSizeSat: UInt64, maxChannelSizeSat: UInt64, satsPerEur: UInt64) { + self.clientBalanceSat = clientBalanceSat + self.existingChannelsTotalSat = existingChannelsTotalSat + self.minChannelSizeSat = minChannelSizeSat + self.maxChannelSizeSat = maxChannelSizeSat + self.satsPerEur = satsPerEur + } +} + +#if compiler(>=6) +extension ChannelLiquidityParams: Sendable {} +#endif + + +extension ChannelLiquidityParams: Equatable, Hashable { + public static func ==(lhs: ChannelLiquidityParams, rhs: ChannelLiquidityParams) -> Bool { + if lhs.clientBalanceSat != rhs.clientBalanceSat { + return false + } + if lhs.existingChannelsTotalSat != rhs.existingChannelsTotalSat { + return false + } + if lhs.minChannelSizeSat != rhs.minChannelSizeSat { + return false + } + if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { + return false + } + if lhs.satsPerEur != rhs.satsPerEur { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(clientBalanceSat) + hasher.combine(existingChannelsTotalSat) + hasher.combine(minChannelSizeSat) + hasher.combine(maxChannelSizeSat) + hasher.combine(satsPerEur) + } +} + +extension ChannelLiquidityParams: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeChannelLiquidityParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ChannelLiquidityParams { + return + try ChannelLiquidityParams( + clientBalanceSat: FfiConverterUInt64.read(from: &buf), + existingChannelsTotalSat: FfiConverterUInt64.read(from: &buf), + minChannelSizeSat: FfiConverterUInt64.read(from: &buf), + maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), + satsPerEur: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: ChannelLiquidityParams, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) + FfiConverterUInt64.write(value.existingChannelsTotalSat, into: &buf) + FfiConverterUInt64.write(value.minChannelSizeSat, into: &buf) + FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) + FfiConverterUInt64.write(value.satsPerEur, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChannelLiquidityParams_lift(_ buf: RustBuffer) throws -> ChannelLiquidityParams { + return try FfiConverterTypeChannelLiquidityParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeChannelLiquidityParams_lower(_ value: ChannelLiquidityParams) -> RustBuffer { + return FfiConverterTypeChannelLiquidityParams.lower(value) +} + + +public struct ClosedChannelDetails { + public var channelId: String + public var counterpartyNodeId: String + public var fundingTxoTxid: String + public var fundingTxoIndex: UInt32 + public var channelValueSats: UInt64 + public var closedAt: UInt64 + public var outboundCapacityMsat: UInt64 + public var inboundCapacityMsat: UInt64 + public var counterpartyUnspendablePunishmentReserve: UInt64 + public var unspendablePunishmentReserve: UInt64 + public var forwardingFeeProportionalMillionths: UInt32 + public var forwardingFeeBaseMsat: UInt32 + public var channelName: String + public var channelClosureReason: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(channelId: String, counterpartyNodeId: String, fundingTxoTxid: String, fundingTxoIndex: UInt32, channelValueSats: UInt64, closedAt: UInt64, outboundCapacityMsat: UInt64, inboundCapacityMsat: UInt64, counterpartyUnspendablePunishmentReserve: UInt64, unspendablePunishmentReserve: UInt64, forwardingFeeProportionalMillionths: UInt32, forwardingFeeBaseMsat: UInt32, channelName: String, channelClosureReason: String) { + self.channelId = channelId + self.counterpartyNodeId = counterpartyNodeId + self.fundingTxoTxid = fundingTxoTxid + self.fundingTxoIndex = fundingTxoIndex + self.channelValueSats = channelValueSats + self.closedAt = closedAt + self.outboundCapacityMsat = outboundCapacityMsat + self.inboundCapacityMsat = inboundCapacityMsat + self.counterpartyUnspendablePunishmentReserve = counterpartyUnspendablePunishmentReserve + self.unspendablePunishmentReserve = unspendablePunishmentReserve + self.forwardingFeeProportionalMillionths = forwardingFeeProportionalMillionths + self.forwardingFeeBaseMsat = forwardingFeeBaseMsat + self.channelName = channelName + self.channelClosureReason = channelClosureReason + } +} + +#if compiler(>=6) +extension ClosedChannelDetails: Sendable {} +#endif + + +extension ClosedChannelDetails: Equatable, Hashable { + public static func ==(lhs: ClosedChannelDetails, rhs: ClosedChannelDetails) -> Bool { + if lhs.channelId != rhs.channelId { + return false + } + if lhs.counterpartyNodeId != rhs.counterpartyNodeId { + return false + } + if lhs.fundingTxoTxid != rhs.fundingTxoTxid { + return false + } + if lhs.fundingTxoIndex != rhs.fundingTxoIndex { + return false + } + if lhs.channelValueSats != rhs.channelValueSats { + return false + } + if lhs.closedAt != rhs.closedAt { + return false + } + if lhs.outboundCapacityMsat != rhs.outboundCapacityMsat { + return false + } + if lhs.inboundCapacityMsat != rhs.inboundCapacityMsat { + return false + } + if lhs.counterpartyUnspendablePunishmentReserve != rhs.counterpartyUnspendablePunishmentReserve { + return false + } + if lhs.unspendablePunishmentReserve != rhs.unspendablePunishmentReserve { + return false + } + if lhs.forwardingFeeProportionalMillionths != rhs.forwardingFeeProportionalMillionths { + return false + } + if lhs.forwardingFeeBaseMsat != rhs.forwardingFeeBaseMsat { + return false + } + if lhs.channelName != rhs.channelName { + return false + } + if lhs.channelClosureReason != rhs.channelClosureReason { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(channelId) + hasher.combine(counterpartyNodeId) + hasher.combine(fundingTxoTxid) + hasher.combine(fundingTxoIndex) + hasher.combine(channelValueSats) + hasher.combine(closedAt) + hasher.combine(outboundCapacityMsat) + hasher.combine(inboundCapacityMsat) + hasher.combine(counterpartyUnspendablePunishmentReserve) + hasher.combine(unspendablePunishmentReserve) + hasher.combine(forwardingFeeProportionalMillionths) + hasher.combine(forwardingFeeBaseMsat) + hasher.combine(channelName) + hasher.combine(channelClosureReason) + } +} + +extension ClosedChannelDetails: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeClosedChannelDetails: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ClosedChannelDetails { + return + try ClosedChannelDetails( + channelId: FfiConverterString.read(from: &buf), + counterpartyNodeId: FfiConverterString.read(from: &buf), + fundingTxoTxid: FfiConverterString.read(from: &buf), + fundingTxoIndex: FfiConverterUInt32.read(from: &buf), + channelValueSats: FfiConverterUInt64.read(from: &buf), + closedAt: FfiConverterUInt64.read(from: &buf), + outboundCapacityMsat: FfiConverterUInt64.read(from: &buf), + inboundCapacityMsat: FfiConverterUInt64.read(from: &buf), + counterpartyUnspendablePunishmentReserve: FfiConverterUInt64.read(from: &buf), + unspendablePunishmentReserve: FfiConverterUInt64.read(from: &buf), + forwardingFeeProportionalMillionths: FfiConverterUInt32.read(from: &buf), + forwardingFeeBaseMsat: FfiConverterUInt32.read(from: &buf), + channelName: FfiConverterString.read(from: &buf), + channelClosureReason: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: ClosedChannelDetails, into buf: inout [UInt8]) { + FfiConverterString.write(value.channelId, into: &buf) + FfiConverterString.write(value.counterpartyNodeId, into: &buf) + FfiConverterString.write(value.fundingTxoTxid, into: &buf) + FfiConverterUInt32.write(value.fundingTxoIndex, into: &buf) + FfiConverterUInt64.write(value.channelValueSats, into: &buf) + FfiConverterUInt64.write(value.closedAt, into: &buf) + FfiConverterUInt64.write(value.outboundCapacityMsat, into: &buf) + FfiConverterUInt64.write(value.inboundCapacityMsat, into: &buf) + FfiConverterUInt64.write(value.counterpartyUnspendablePunishmentReserve, into: &buf) + FfiConverterUInt64.write(value.unspendablePunishmentReserve, into: &buf) + FfiConverterUInt32.write(value.forwardingFeeProportionalMillionths, into: &buf) + FfiConverterUInt32.write(value.forwardingFeeBaseMsat, into: &buf) + FfiConverterString.write(value.channelName, into: &buf) + FfiConverterString.write(value.channelClosureReason, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeClosedChannelDetails_lift(_ buf: RustBuffer) throws -> ClosedChannelDetails { + return try FfiConverterTypeClosedChannelDetails.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeClosedChannelDetails_lower(_ value: ClosedChannelDetails) -> RustBuffer { + return FfiConverterTypeClosedChannelDetails.lower(value) +} + + +/** + * A finalized transaction ready to broadcast. + */ +public struct CompletedTransaction { + /** + * Consensus-encoded transaction hex. + */ + public var serializedTx: String + /** + * Canonical transaction id, excluding witness data. + */ + public var txid: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Consensus-encoded transaction hex. + */serializedTx: String, + /** + * Canonical transaction id, excluding witness data. + */txid: String) { + self.serializedTx = serializedTx + self.txid = txid + } +} + +#if compiler(>=6) +extension CompletedTransaction: Sendable {} +#endif + + +extension CompletedTransaction: Equatable, Hashable { + public static func ==(lhs: CompletedTransaction, rhs: CompletedTransaction) -> Bool { + if lhs.serializedTx != rhs.serializedTx { + return false + } + if lhs.txid != rhs.txid { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(serializedTx) + hasher.combine(txid) + } +} + +extension CompletedTransaction: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCompletedTransaction: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CompletedTransaction { + return + try CompletedTransaction( + serializedTx: FfiConverterString.read(from: &buf), + txid: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: CompletedTransaction, into buf: inout [UInt8]) { + FfiConverterString.write(value.serializedTx, into: &buf) + FfiConverterString.write(value.txid, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCompletedTransaction_lift(_ buf: RustBuffer) throws -> CompletedTransaction { + return try FfiConverterTypeCompletedTransaction.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCompletedTransaction_lower(_ value: CompletedTransaction) -> RustBuffer { + return FfiConverterTypeCompletedTransaction.lower(value) +} + + /** - * Grouped address lists for an account. + * Full account structure with addresses and UTXOs. */ -public struct AccountAddresses { +public struct ComposeAccount { /** - * Used receive addresses (have at least one transaction) + * Account derivation path (e.g., "m/84'/0'/0'") */ - public var used: [AddressInfo] + public var path: String /** - * Unused receive addresses (no transactions yet) + * Categorized addresses */ - public var unused: [AddressInfo] + public var addresses: AccountAddresses /** - * Change addresses + * Unspent transaction outputs */ - public var change: [AddressInfo] + public var utxo: [AccountUtxo] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Account derivation path (e.g., "m/84'/0'/0'") + */path: String, + /** + * Categorized addresses + */addresses: AccountAddresses, + /** + * Unspent transaction outputs + */utxo: [AccountUtxo]) { + self.path = path + self.addresses = addresses + self.utxo = utxo + } +} + +#if compiler(>=6) +extension ComposeAccount: Sendable {} +#endif + + +extension ComposeAccount: Equatable, Hashable { + public static func ==(lhs: ComposeAccount, rhs: ComposeAccount) -> Bool { + if lhs.path != rhs.path { + return false + } + if lhs.addresses != rhs.addresses { + return false + } + if lhs.utxo != rhs.utxo { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(path) + hasher.combine(addresses) + hasher.combine(utxo) + } +} + +extension ComposeAccount: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeComposeAccount: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeAccount { + return + try ComposeAccount( + path: FfiConverterString.read(from: &buf), + addresses: FfiConverterTypeAccountAddresses.read(from: &buf), + utxo: FfiConverterSequenceTypeAccountUtxo.read(from: &buf) + ) + } + + public static func write(_ value: ComposeAccount, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterTypeAccountAddresses.write(value.addresses, into: &buf) + FfiConverterSequenceTypeAccountUtxo.write(value.utxo, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeComposeAccount_lift(_ buf: RustBuffer) throws -> ComposeAccount { + return try FfiConverterTypeComposeAccount.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeComposeAccount_lower(_ value: ComposeAccount) -> RustBuffer { + return FfiConverterTypeComposeAccount.lower(value) +} + + +/** + * Parameters for composing a signer-agnostic transaction. + */ +public struct ComposeParams { + /** + * Wallet configuration (key, server, network) + */ + public var wallet: WalletParams + /** + * Desired transaction outputs + */ + public var outputs: [ComposeOutput] + /** + * Fee rates to evaluate (sat/vB), one PSBT per rate + */ + public var feeRates: [Float] + /** + * UTXO selection strategy (defaults to BranchAndBound) + */ + public var coinSelection: CoinSelection? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Wallet configuration (key, server, network) + */wallet: WalletParams, + /** + * Desired transaction outputs + */outputs: [ComposeOutput], + /** + * Fee rates to evaluate (sat/vB), one PSBT per rate + */feeRates: [Float], + /** + * UTXO selection strategy (defaults to BranchAndBound) + */coinSelection: CoinSelection?) { + self.wallet = wallet + self.outputs = outputs + self.feeRates = feeRates + self.coinSelection = coinSelection + } +} + +#if compiler(>=6) +extension ComposeParams: Sendable {} +#endif + + +extension ComposeParams: Equatable, Hashable { + public static func ==(lhs: ComposeParams, rhs: ComposeParams) -> Bool { + if lhs.wallet != rhs.wallet { + return false + } + if lhs.outputs != rhs.outputs { + return false + } + if lhs.feeRates != rhs.feeRates { + return false + } + if lhs.coinSelection != rhs.coinSelection { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(wallet) + hasher.combine(outputs) + hasher.combine(feeRates) + hasher.combine(coinSelection) + } +} + +extension ComposeParams: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeComposeParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeParams { + return + try ComposeParams( + wallet: FfiConverterTypeWalletParams.read(from: &buf), + outputs: FfiConverterSequenceTypeComposeOutput.read(from: &buf), + feeRates: FfiConverterSequenceFloat.read(from: &buf), + coinSelection: FfiConverterOptionTypeCoinSelection.read(from: &buf) + ) + } + + public static func write(_ value: ComposeParams, into buf: inout [UInt8]) { + FfiConverterTypeWalletParams.write(value.wallet, into: &buf) + FfiConverterSequenceTypeComposeOutput.write(value.outputs, into: &buf) + FfiConverterSequenceFloat.write(value.feeRates, into: &buf) + FfiConverterOptionTypeCoinSelection.write(value.coinSelection, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeComposeParams_lift(_ buf: RustBuffer) throws -> ComposeParams { + return try FfiConverterTypeComposeParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeComposeParams_lower(_ value: ComposeParams) -> RustBuffer { + return FfiConverterTypeComposeParams.lower(value) +} + + +public struct CreateCjitOptions { + public var source: String? + public var discountCode: String? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(source: String?, discountCode: String?) { + self.source = source + self.discountCode = discountCode + } +} + +#if compiler(>=6) +extension CreateCjitOptions: Sendable {} +#endif + + +extension CreateCjitOptions: Equatable, Hashable { + public static func ==(lhs: CreateCjitOptions, rhs: CreateCjitOptions) -> Bool { + if lhs.source != rhs.source { + return false + } + if lhs.discountCode != rhs.discountCode { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(source) + hasher.combine(discountCode) + } +} + +extension CreateCjitOptions: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeCreateCjitOptions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateCjitOptions { + return + try CreateCjitOptions( + source: FfiConverterOptionString.read(from: &buf), + discountCode: FfiConverterOptionString.read(from: &buf) + ) + } + + public static func write(_ value: CreateCjitOptions, into buf: inout [UInt8]) { + FfiConverterOptionString.write(value.source, into: &buf) + FfiConverterOptionString.write(value.discountCode, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCreateCjitOptions_lift(_ buf: RustBuffer) throws -> CreateCjitOptions { + return try FfiConverterTypeCreateCjitOptions.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeCreateCjitOptions_lower(_ value: CreateCjitOptions) -> RustBuffer { + return FfiConverterTypeCreateCjitOptions.lower(value) +} + + +public struct CreateOrderOptions { + public var clientBalanceSat: UInt64 + public var lspNodeId: String? + public var couponCode: String + public var source: String? + public var discountCode: String? + public var zeroConf: Bool + public var zeroConfPayment: Bool? + public var zeroReserve: Bool + public var clientNodeId: String? + public var signature: String? + public var timestamp: String? + public var refundOnchainAddress: String? + public var announceChannel: Bool // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Used receive addresses (have at least one transaction) - */used: [AddressInfo], - /** - * Unused receive addresses (no transactions yet) - */unused: [AddressInfo], - /** - * Change addresses - */change: [AddressInfo]) { - self.used = used - self.unused = unused - self.change = change + public init(clientBalanceSat: UInt64, lspNodeId: String?, couponCode: String, source: String?, discountCode: String?, zeroConf: Bool, zeroConfPayment: Bool?, zeroReserve: Bool, clientNodeId: String?, signature: String?, timestamp: String?, refundOnchainAddress: String?, announceChannel: Bool) { + self.clientBalanceSat = clientBalanceSat + self.lspNodeId = lspNodeId + self.couponCode = couponCode + self.source = source + self.discountCode = discountCode + self.zeroConf = zeroConf + self.zeroConfPayment = zeroConfPayment + self.zeroReserve = zeroReserve + self.clientNodeId = clientNodeId + self.signature = signature + self.timestamp = timestamp + self.refundOnchainAddress = refundOnchainAddress + self.announceChannel = announceChannel } } #if compiler(>=6) -extension AccountAddresses: Sendable {} +extension CreateOrderOptions: Sendable {} #endif -extension AccountAddresses: Equatable, Hashable { - public static func ==(lhs: AccountAddresses, rhs: AccountAddresses) -> Bool { - if lhs.used != rhs.used { +extension CreateOrderOptions: Equatable, Hashable { + public static func ==(lhs: CreateOrderOptions, rhs: CreateOrderOptions) -> Bool { + if lhs.clientBalanceSat != rhs.clientBalanceSat { return false } - if lhs.unused != rhs.unused { + if lhs.lspNodeId != rhs.lspNodeId { return false } - if lhs.change != rhs.change { + if lhs.couponCode != rhs.couponCode { + return false + } + if lhs.source != rhs.source { + return false + } + if lhs.discountCode != rhs.discountCode { + return false + } + if lhs.zeroConf != rhs.zeroConf { + return false + } + if lhs.zeroConfPayment != rhs.zeroConfPayment { + return false + } + if lhs.zeroReserve != rhs.zeroReserve { + return false + } + if lhs.clientNodeId != rhs.clientNodeId { + return false + } + if lhs.signature != rhs.signature { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.refundOnchainAddress != rhs.refundOnchainAddress { + return false + } + if lhs.announceChannel != rhs.announceChannel { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(used) - hasher.combine(unused) - hasher.combine(change) + hasher.combine(clientBalanceSat) + hasher.combine(lspNodeId) + hasher.combine(couponCode) + hasher.combine(source) + hasher.combine(discountCode) + hasher.combine(zeroConf) + hasher.combine(zeroConfPayment) + hasher.combine(zeroReserve) + hasher.combine(clientNodeId) + hasher.combine(signature) + hasher.combine(timestamp) + hasher.combine(refundOnchainAddress) + hasher.combine(announceChannel) } } -extension AccountAddresses: Codable {} +extension CreateOrderOptions: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAccountAddresses: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountAddresses { +public struct FfiConverterTypeCreateOrderOptions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateOrderOptions { return - try AccountAddresses( - used: FfiConverterSequenceTypeAddressInfo.read(from: &buf), - unused: FfiConverterSequenceTypeAddressInfo.read(from: &buf), - change: FfiConverterSequenceTypeAddressInfo.read(from: &buf) + try CreateOrderOptions( + clientBalanceSat: FfiConverterUInt64.read(from: &buf), + lspNodeId: FfiConverterOptionString.read(from: &buf), + couponCode: FfiConverterString.read(from: &buf), + source: FfiConverterOptionString.read(from: &buf), + discountCode: FfiConverterOptionString.read(from: &buf), + zeroConf: FfiConverterBool.read(from: &buf), + zeroConfPayment: FfiConverterOptionBool.read(from: &buf), + zeroReserve: FfiConverterBool.read(from: &buf), + clientNodeId: FfiConverterOptionString.read(from: &buf), + signature: FfiConverterOptionString.read(from: &buf), + timestamp: FfiConverterOptionString.read(from: &buf), + refundOnchainAddress: FfiConverterOptionString.read(from: &buf), + announceChannel: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: AccountAddresses, into buf: inout [UInt8]) { - FfiConverterSequenceTypeAddressInfo.write(value.used, into: &buf) - FfiConverterSequenceTypeAddressInfo.write(value.unused, into: &buf) - FfiConverterSequenceTypeAddressInfo.write(value.change, into: &buf) + public static func write(_ value: CreateOrderOptions, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) + FfiConverterOptionString.write(value.lspNodeId, into: &buf) + FfiConverterString.write(value.couponCode, into: &buf) + FfiConverterOptionString.write(value.source, into: &buf) + FfiConverterOptionString.write(value.discountCode, into: &buf) + FfiConverterBool.write(value.zeroConf, into: &buf) + FfiConverterOptionBool.write(value.zeroConfPayment, into: &buf) + FfiConverterBool.write(value.zeroReserve, into: &buf) + FfiConverterOptionString.write(value.clientNodeId, into: &buf) + FfiConverterOptionString.write(value.signature, into: &buf) + FfiConverterOptionString.write(value.timestamp, into: &buf) + FfiConverterOptionString.write(value.refundOnchainAddress, into: &buf) + FfiConverterBool.write(value.announceChannel, into: &buf) } } @@ -2650,128 +4879,79 @@ public struct FfiConverterTypeAccountAddresses: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountAddresses_lift(_ buf: RustBuffer) throws -> AccountAddresses { - return try FfiConverterTypeAccountAddresses.lift(buf) +public func FfiConverterTypeCreateOrderOptions_lift(_ buf: RustBuffer) throws -> CreateOrderOptions { + return try FfiConverterTypeCreateOrderOptions.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountAddresses_lower(_ value: AccountAddresses) -> RustBuffer { - return FfiConverterTypeAccountAddresses.lower(value) +public func FfiConverterTypeCreateOrderOptions_lower(_ value: CreateOrderOptions) -> RustBuffer { + return FfiConverterTypeCreateOrderOptions.lower(value) } -/** - * Result from querying an extended public key via Electrum. - */ -public struct AccountInfoResult { - /** - * The account structure with addresses and UTXOs - */ - public var account: ComposeAccount - /** - * Total confirmed balance in satoshis - */ - public var balance: UInt64 - /** - * Number of UTXOs - */ - public var utxoCount: UInt32 - /** - * The detected or specified account type - */ - public var accountType: AccountType - /** - * The current blockchain tip height - */ - public var blockHeight: UInt32 +public struct DefaultLspBalanceParams { + public var clientBalanceSat: UInt64 + public var maxChannelSizeSat: UInt64 + public var satsPerEur: UInt64 - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * The account structure with addresses and UTXOs - */account: ComposeAccount, - /** - * Total confirmed balance in satoshis - */balance: UInt64, - /** - * Number of UTXOs - */utxoCount: UInt32, - /** - * The detected or specified account type - */accountType: AccountType, - /** - * The current blockchain tip height - */blockHeight: UInt32) { - self.account = account - self.balance = balance - self.utxoCount = utxoCount - self.accountType = accountType - self.blockHeight = blockHeight + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(clientBalanceSat: UInt64, maxChannelSizeSat: UInt64, satsPerEur: UInt64) { + self.clientBalanceSat = clientBalanceSat + self.maxChannelSizeSat = maxChannelSizeSat + self.satsPerEur = satsPerEur } } #if compiler(>=6) -extension AccountInfoResult: Sendable {} +extension DefaultLspBalanceParams: Sendable {} #endif -extension AccountInfoResult: Equatable, Hashable { - public static func ==(lhs: AccountInfoResult, rhs: AccountInfoResult) -> Bool { - if lhs.account != rhs.account { - return false - } - if lhs.balance != rhs.balance { - return false - } - if lhs.utxoCount != rhs.utxoCount { +extension DefaultLspBalanceParams: Equatable, Hashable { + public static func ==(lhs: DefaultLspBalanceParams, rhs: DefaultLspBalanceParams) -> Bool { + if lhs.clientBalanceSat != rhs.clientBalanceSat { return false } - if lhs.accountType != rhs.accountType { + if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { return false } - if lhs.blockHeight != rhs.blockHeight { + if lhs.satsPerEur != rhs.satsPerEur { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(account) - hasher.combine(balance) - hasher.combine(utxoCount) - hasher.combine(accountType) - hasher.combine(blockHeight) + hasher.combine(clientBalanceSat) + hasher.combine(maxChannelSizeSat) + hasher.combine(satsPerEur) } } -extension AccountInfoResult: Codable {} +extension DefaultLspBalanceParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAccountInfoResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoResult { +public struct FfiConverterTypeDefaultLspBalanceParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DefaultLspBalanceParams { return - try AccountInfoResult( - account: FfiConverterTypeComposeAccount.read(from: &buf), - balance: FfiConverterUInt64.read(from: &buf), - utxoCount: FfiConverterUInt32.read(from: &buf), - accountType: FfiConverterTypeAccountType.read(from: &buf), - blockHeight: FfiConverterUInt32.read(from: &buf) + try DefaultLspBalanceParams( + clientBalanceSat: FfiConverterUInt64.read(from: &buf), + maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), + satsPerEur: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: AccountInfoResult, into buf: inout [UInt8]) { - FfiConverterTypeComposeAccount.write(value.account, into: &buf) - FfiConverterUInt64.write(value.balance, into: &buf) - FfiConverterUInt32.write(value.utxoCount, into: &buf) - FfiConverterTypeAccountType.write(value.accountType, into: &buf) - FfiConverterUInt32.write(value.blockHeight, into: &buf) + public static func write(_ value: DefaultLspBalanceParams, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) + FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) + FfiConverterUInt64.write(value.satsPerEur, into: &buf) } } @@ -2779,198 +4959,63 @@ public struct FfiConverterTypeAccountInfoResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoResult_lift(_ buf: RustBuffer) throws -> AccountInfoResult { - return try FfiConverterTypeAccountInfoResult.lift(buf) +public func FfiConverterTypeDefaultLspBalanceParams_lift(_ buf: RustBuffer) throws -> DefaultLspBalanceParams { + return try FfiConverterTypeDefaultLspBalanceParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoResult_lower(_ value: AccountInfoResult) -> RustBuffer { - return FfiConverterTypeAccountInfoResult.lower(value) +public func FfiConverterTypeDefaultLspBalanceParams_lower(_ value: DefaultLspBalanceParams) -> RustBuffer { + return FfiConverterTypeDefaultLspBalanceParams.lower(value) } -/** - * A UTXO associated with an account or address. - */ -public struct AccountUtxo { - /** - * Transaction ID (hex) - */ - public var txid: String - /** - * Output index - */ - public var vout: UInt32 - /** - * Amount in satoshis - */ - public var amount: UInt64 - /** - * Block height where the UTXO was confirmed (0 if unconfirmed) - */ - public var blockHeight: UInt32 - /** - * Address holding this UTXO - */ - public var address: String - /** - * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") - */ - public var path: String - /** - * Number of confirmations (0 if unconfirmed) - */ - public var confirmations: UInt32 - /** - * Whether this is a coinbase output - */ - public var coinbase: Bool - /** - * Whether this UTXO is owned by the account - */ - public var own: Bool - /** - * Whether this UTXO must be included in the transaction - */ - public var required: Bool? +public struct ErrorData { + public var errorDetails: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Transaction ID (hex) - */txid: String, - /** - * Output index - */vout: UInt32, - /** - * Amount in satoshis - */amount: UInt64, - /** - * Block height where the UTXO was confirmed (0 if unconfirmed) - */blockHeight: UInt32, - /** - * Address holding this UTXO - */address: String, - /** - * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") - */path: String, - /** - * Number of confirmations (0 if unconfirmed) - */confirmations: UInt32, - /** - * Whether this is a coinbase output - */coinbase: Bool, - /** - * Whether this UTXO is owned by the account - */own: Bool, - /** - * Whether this UTXO must be included in the transaction - */required: Bool?) { - self.txid = txid - self.vout = vout - self.amount = amount - self.blockHeight = blockHeight - self.address = address - self.path = path - self.confirmations = confirmations - self.coinbase = coinbase - self.own = own - self.required = required + public init(errorDetails: String) { + self.errorDetails = errorDetails } } #if compiler(>=6) -extension AccountUtxo: Sendable {} +extension ErrorData: Sendable {} #endif -extension AccountUtxo: Equatable, Hashable { - public static func ==(lhs: AccountUtxo, rhs: AccountUtxo) -> Bool { - if lhs.txid != rhs.txid { - return false - } - if lhs.vout != rhs.vout { - return false - } - if lhs.amount != rhs.amount { - return false - } - if lhs.blockHeight != rhs.blockHeight { - return false - } - if lhs.address != rhs.address { - return false - } - if lhs.path != rhs.path { - return false - } - if lhs.confirmations != rhs.confirmations { - return false - } - if lhs.coinbase != rhs.coinbase { - return false - } - if lhs.own != rhs.own { - return false - } - if lhs.required != rhs.required { +extension ErrorData: Equatable, Hashable { + public static func ==(lhs: ErrorData, rhs: ErrorData) -> Bool { + if lhs.errorDetails != rhs.errorDetails { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(vout) - hasher.combine(amount) - hasher.combine(blockHeight) - hasher.combine(address) - hasher.combine(path) - hasher.combine(confirmations) - hasher.combine(coinbase) - hasher.combine(own) - hasher.combine(required) + hasher.combine(errorDetails) } } -extension AccountUtxo: Codable {} +extension ErrorData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAccountUtxo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountUtxo { +public struct FfiConverterTypeErrorData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ErrorData { return - try AccountUtxo( - txid: FfiConverterString.read(from: &buf), - vout: FfiConverterUInt32.read(from: &buf), - amount: FfiConverterUInt64.read(from: &buf), - blockHeight: FfiConverterUInt32.read(from: &buf), - address: FfiConverterString.read(from: &buf), - path: FfiConverterString.read(from: &buf), - confirmations: FfiConverterUInt32.read(from: &buf), - coinbase: FfiConverterBool.read(from: &buf), - own: FfiConverterBool.read(from: &buf), - required: FfiConverterOptionBool.read(from: &buf) + try ErrorData( + errorDetails: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: AccountUtxo, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt32.write(value.vout, into: &buf) - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterUInt32.write(value.blockHeight, into: &buf) - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.path, into: &buf) - FfiConverterUInt32.write(value.confirmations, into: &buf) - FfiConverterBool.write(value.coinbase, into: &buf) - FfiConverterBool.write(value.own, into: &buf) - FfiConverterOptionBool.write(value.required, into: &buf) + public static func write(_ value: ErrorData, into buf: inout [UInt8]) { + FfiConverterString.write(value.errorDetails, into: &buf) } } @@ -2978,79 +5023,79 @@ public struct FfiConverterTypeAccountUtxo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountUtxo_lift(_ buf: RustBuffer) throws -> AccountUtxo { - return try FfiConverterTypeAccountUtxo.lift(buf) +public func FfiConverterTypeErrorData_lift(_ buf: RustBuffer) throws -> ErrorData { + return try FfiConverterTypeErrorData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountUtxo_lower(_ value: AccountUtxo) -> RustBuffer { - return FfiConverterTypeAccountUtxo.lower(value) +public func FfiConverterTypeErrorData_lower(_ value: ErrorData) -> RustBuffer { + return FfiConverterTypeErrorData.lower(value) } -public struct ActivityTags { - public var walletId: String - public var activityId: String - public var tags: [String] - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(walletId: String, activityId: String, tags: [String]) { - self.walletId = walletId - self.activityId = activityId - self.tags = tags +public struct FeeRates { + public var fast: UInt32 + public var mid: UInt32 + public var slow: UInt32 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(fast: UInt32, mid: UInt32, slow: UInt32) { + self.fast = fast + self.mid = mid + self.slow = slow } } #if compiler(>=6) -extension ActivityTags: Sendable {} +extension FeeRates: Sendable {} #endif -extension ActivityTags: Equatable, Hashable { - public static func ==(lhs: ActivityTags, rhs: ActivityTags) -> Bool { - if lhs.walletId != rhs.walletId { +extension FeeRates: Equatable, Hashable { + public static func ==(lhs: FeeRates, rhs: FeeRates) -> Bool { + if lhs.fast != rhs.fast { return false } - if lhs.activityId != rhs.activityId { + if lhs.mid != rhs.mid { return false } - if lhs.tags != rhs.tags { + if lhs.slow != rhs.slow { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(walletId) - hasher.combine(activityId) - hasher.combine(tags) + hasher.combine(fast) + hasher.combine(mid) + hasher.combine(slow) } } -extension ActivityTags: Codable {} +extension FeeRates: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivityTags: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityTags { +public struct FfiConverterTypeFeeRates: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FeeRates { return - try ActivityTags( - walletId: FfiConverterString.read(from: &buf), - activityId: FfiConverterString.read(from: &buf), - tags: FfiConverterSequenceString.read(from: &buf) + try FeeRates( + fast: FfiConverterUInt32.read(from: &buf), + mid: FfiConverterUInt32.read(from: &buf), + slow: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: ActivityTags, into buf: inout [UInt8]) { - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.activityId, into: &buf) - FfiConverterSequenceString.write(value.tags, into: &buf) + public static func write(_ value: FeeRates, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.fast, into: &buf) + FfiConverterUInt32.write(value.mid, into: &buf) + FfiConverterUInt32.write(value.slow, into: &buf) } } @@ -3058,100 +5103,71 @@ public struct FfiConverterTypeActivityTags: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityTags_lift(_ buf: RustBuffer) throws -> ActivityTags { - return try FfiConverterTypeActivityTags.lift(buf) +public func FfiConverterTypeFeeRates_lift(_ buf: RustBuffer) throws -> FeeRates { + return try FfiConverterTypeFeeRates.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityTags_lower(_ value: ActivityTags) -> RustBuffer { - return FfiConverterTypeActivityTags.lower(value) +public func FfiConverterTypeFeeRates_lower(_ value: FeeRates) -> RustBuffer { + return FfiConverterTypeFeeRates.lower(value) } -/** - * Information about a single address in an account. - */ -public struct AddressInfo { - /** - * The Bitcoin address - */ - public var address: String - /** - * BIP32 derivation path - */ - public var path: String - /** - * Number of transfers (real count in `get_address_info`, 1/0 presence flag in `get_account_info`) - */ - public var transfers: UInt32 +public struct FundingTx { + public var id: String + public var vout: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * The Bitcoin address - */address: String, - /** - * BIP32 derivation path - */path: String, - /** - * Number of transfers (real count in `get_address_info`, 1/0 presence flag in `get_account_info`) - */transfers: UInt32) { - self.address = address - self.path = path - self.transfers = transfers + public init(id: String, vout: UInt64) { + self.id = id + self.vout = vout } } #if compiler(>=6) -extension AddressInfo: Sendable {} +extension FundingTx: Sendable {} #endif -extension AddressInfo: Equatable, Hashable { - public static func ==(lhs: AddressInfo, rhs: AddressInfo) -> Bool { - if lhs.address != rhs.address { - return false - } - if lhs.path != rhs.path { +extension FundingTx: Equatable, Hashable { + public static func ==(lhs: FundingTx, rhs: FundingTx) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.transfers != rhs.transfers { + if lhs.vout != rhs.vout { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(path) - hasher.combine(transfers) + hasher.combine(id) + hasher.combine(vout) } } -extension AddressInfo: Codable {} +extension FundingTx: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAddressInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressInfo { +public struct FfiConverterTypeFundingTx: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FundingTx { return - try AddressInfo( - address: FfiConverterString.read(from: &buf), - path: FfiConverterString.read(from: &buf), - transfers: FfiConverterUInt32.read(from: &buf) + try FundingTx( + id: FfiConverterString.read(from: &buf), + vout: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: AddressInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.path, into: &buf) - FfiConverterUInt32.write(value.transfers, into: &buf) + public static func write(_ value: FundingTx, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterUInt64.write(value.vout, into: &buf) } } @@ -3159,369 +5175,167 @@ public struct FfiConverterTypeAddressInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressInfo_lift(_ buf: RustBuffer) throws -> AddressInfo { - return try FfiConverterTypeAddressInfo.lift(buf) +public func FfiConverterTypeFundingTx_lift(_ buf: RustBuffer) throws -> FundingTx { + return try FfiConverterTypeFundingTx.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressInfo_lower(_ value: AddressInfo) -> RustBuffer { - return FfiConverterTypeAddressInfo.lower(value) +public func FfiConverterTypeFundingTx_lower(_ value: FundingTx) -> RustBuffer { + return FfiConverterTypeFundingTx.lower(value) } -/** - * Fees and limits for a swap pair, used to size a swap and present costs. - */ -public struct BoltzPairInfo { - /** - * Pair hash identifying the current terms (passed back to Boltz if needed). - */ - public var hash: String - /** - * Exchange rate of the pair. - */ - public var rate: Double - /** - * Minimum swap amount in satoshis. - */ - public var minimalSat: UInt64 +public struct GetAddressResponse { /** - * Maximum swap amount in satoshis. + * The generated Bitcoin address as a string */ - public var maximalSat: UInt64 + public var address: String /** - * Boltz service fee as a percentage of the swap amount. + * The derivation path used to generate the address */ - public var feePercentage: Double + public var path: String /** - * Estimated absolute miner fees in satoshis. + * The hexadecimal representation of the public key */ - public var minerFeesSat: UInt64 + public var publicKey: String // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Pair hash identifying the current terms (passed back to Boltz if needed). - */hash: String, - /** - * Exchange rate of the pair. - */rate: Double, - /** - * Minimum swap amount in satoshis. - */minimalSat: UInt64, - /** - * Maximum swap amount in satoshis. - */maximalSat: UInt64, + * The generated Bitcoin address as a string + */address: String, /** - * Boltz service fee as a percentage of the swap amount. - */feePercentage: Double, + * The derivation path used to generate the address + */path: String, /** - * Estimated absolute miner fees in satoshis. - */minerFeesSat: UInt64) { - self.hash = hash - self.rate = rate - self.minimalSat = minimalSat - self.maximalSat = maximalSat - self.feePercentage = feePercentage - self.minerFeesSat = minerFeesSat + * The hexadecimal representation of the public key + */publicKey: String) { + self.address = address + self.path = path + self.publicKey = publicKey } } #if compiler(>=6) -extension BoltzPairInfo: Sendable {} +extension GetAddressResponse: Sendable {} #endif -extension BoltzPairInfo: Equatable, Hashable { - public static func ==(lhs: BoltzPairInfo, rhs: BoltzPairInfo) -> Bool { - if lhs.hash != rhs.hash { - return false - } - if lhs.rate != rhs.rate { - return false - } - if lhs.minimalSat != rhs.minimalSat { - return false - } - if lhs.maximalSat != rhs.maximalSat { +extension GetAddressResponse: Equatable, Hashable { + public static func ==(lhs: GetAddressResponse, rhs: GetAddressResponse) -> Bool { + if lhs.address != rhs.address { return false } - if lhs.feePercentage != rhs.feePercentage { + if lhs.path != rhs.path { return false } - if lhs.minerFeesSat != rhs.minerFeesSat { + if lhs.publicKey != rhs.publicKey { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(hash) - hasher.combine(rate) - hasher.combine(minimalSat) - hasher.combine(maximalSat) - hasher.combine(feePercentage) - hasher.combine(minerFeesSat) + hasher.combine(address) + hasher.combine(path) + hasher.combine(publicKey) } } -extension BoltzPairInfo: Codable {} +extension GetAddressResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzPairInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzPairInfo { +public struct FfiConverterTypeGetAddressResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAddressResponse { return - try BoltzPairInfo( - hash: FfiConverterString.read(from: &buf), - rate: FfiConverterDouble.read(from: &buf), - minimalSat: FfiConverterUInt64.read(from: &buf), - maximalSat: FfiConverterUInt64.read(from: &buf), - feePercentage: FfiConverterDouble.read(from: &buf), - minerFeesSat: FfiConverterUInt64.read(from: &buf) + try GetAddressResponse( + address: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + publicKey: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: BoltzPairInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.hash, into: &buf) - FfiConverterDouble.write(value.rate, into: &buf) - FfiConverterUInt64.write(value.minimalSat, into: &buf) - FfiConverterUInt64.write(value.maximalSat, into: &buf) - FfiConverterDouble.write(value.feePercentage, into: &buf) - FfiConverterUInt64.write(value.minerFeesSat, into: &buf) + public static func write(_ value: GetAddressResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterString.write(value.publicKey, into: &buf) } } #if swift(>=5.8) @_documentation(visibility: private) -#endif -public func FfiConverterTypeBoltzPairInfo_lift(_ buf: RustBuffer) throws -> BoltzPairInfo { - return try FfiConverterTypeBoltzPairInfo.lift(buf) +#endif +public func FfiConverterTypeGetAddressResponse_lift(_ buf: RustBuffer) throws -> GetAddressResponse { + return try FfiConverterTypeGetAddressResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzPairInfo_lower(_ value: BoltzPairInfo) -> RustBuffer { - return FfiConverterTypeBoltzPairInfo.lower(value) +public func FfiConverterTypeGetAddressResponse_lower(_ value: GetAddressResponse) -> RustBuffer { + return FfiConverterTypeGetAddressResponse.lower(value) } -/** - * A persisted swap and its current state, returned by the listing/query APIs. - */ -public struct BoltzSwap { - public var id: String - public var swapType: BoltzSwapType - public var status: BoltzSwapStatus - public var network: BoltzNetwork - /** - * Deterministic BIP85 index used to derive this swap's key and preimage - * from the wallet seed. The recovery handle: given the seed and this index - * (or by scanning indices), the swap's secrets can be reconstructed. - */ - public var swapIndex: UInt64 - /** - * For submarine swaps: the amount to lock onchain. For reverse swaps: the - * Lightning invoice amount. - */ - public var amountSat: UInt64 - /** - * For reverse swaps: the onchain amount that will be received. - */ - public var onchainAmountSat: UInt64? - /** - * Lightning invoice associated with the swap (the hold invoice for reverse - * swaps, the invoice Boltz pays for submarine swaps). - */ - public var invoice: String? - /** - * Onchain lockup address. - */ - public var lockupAddress: String? - /** - * The address funds are claimed to (reverse) or refunded to (submarine). - */ - public var onchainAddress: String? - public var timeoutBlockHeight: UInt64 - public var createdAt: UInt64 - /** - * Txid of the claim transaction once broadcast (reverse swaps). - */ - public var claimTxId: String? +public struct GetAddressesResponse { /** - * Txid of the refund transaction once broadcast (submarine swaps). + * Vector of generated Bitcoin addresses */ - public var refundTxId: String? + public var addresses: [GetAddressResponse] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, swapType: BoltzSwapType, status: BoltzSwapStatus, network: BoltzNetwork, - /** - * Deterministic BIP85 index used to derive this swap's key and preimage - * from the wallet seed. The recovery handle: given the seed and this index - * (or by scanning indices), the swap's secrets can be reconstructed. - */swapIndex: UInt64, - /** - * For submarine swaps: the amount to lock onchain. For reverse swaps: the - * Lightning invoice amount. - */amountSat: UInt64, - /** - * For reverse swaps: the onchain amount that will be received. - */onchainAmountSat: UInt64?, - /** - * Lightning invoice associated with the swap (the hold invoice for reverse - * swaps, the invoice Boltz pays for submarine swaps). - */invoice: String?, - /** - * Onchain lockup address. - */lockupAddress: String?, - /** - * The address funds are claimed to (reverse) or refunded to (submarine). - */onchainAddress: String?, timeoutBlockHeight: UInt64, createdAt: UInt64, - /** - * Txid of the claim transaction once broadcast (reverse swaps). - */claimTxId: String?, + public init( /** - * Txid of the refund transaction once broadcast (submarine swaps). - */refundTxId: String?) { - self.id = id - self.swapType = swapType - self.status = status - self.network = network - self.swapIndex = swapIndex - self.amountSat = amountSat - self.onchainAmountSat = onchainAmountSat - self.invoice = invoice - self.lockupAddress = lockupAddress - self.onchainAddress = onchainAddress - self.timeoutBlockHeight = timeoutBlockHeight - self.createdAt = createdAt - self.claimTxId = claimTxId - self.refundTxId = refundTxId + * Vector of generated Bitcoin addresses + */addresses: [GetAddressResponse]) { + self.addresses = addresses } } #if compiler(>=6) -extension BoltzSwap: Sendable {} +extension GetAddressesResponse: Sendable {} #endif -extension BoltzSwap: Equatable, Hashable { - public static func ==(lhs: BoltzSwap, rhs: BoltzSwap) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.swapType != rhs.swapType { - return false - } - if lhs.status != rhs.status { - return false - } - if lhs.network != rhs.network { - return false - } - if lhs.swapIndex != rhs.swapIndex { - return false - } - if lhs.amountSat != rhs.amountSat { - return false - } - if lhs.onchainAmountSat != rhs.onchainAmountSat { - return false - } - if lhs.invoice != rhs.invoice { - return false - } - if lhs.lockupAddress != rhs.lockupAddress { - return false - } - if lhs.onchainAddress != rhs.onchainAddress { - return false - } - if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { - return false - } - if lhs.createdAt != rhs.createdAt { - return false - } - if lhs.claimTxId != rhs.claimTxId { - return false - } - if lhs.refundTxId != rhs.refundTxId { +extension GetAddressesResponse: Equatable, Hashable { + public static func ==(lhs: GetAddressesResponse, rhs: GetAddressesResponse) -> Bool { + if lhs.addresses != rhs.addresses { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(swapType) - hasher.combine(status) - hasher.combine(network) - hasher.combine(swapIndex) - hasher.combine(amountSat) - hasher.combine(onchainAmountSat) - hasher.combine(invoice) - hasher.combine(lockupAddress) - hasher.combine(onchainAddress) - hasher.combine(timeoutBlockHeight) - hasher.combine(createdAt) - hasher.combine(claimTxId) - hasher.combine(refundTxId) + hasher.combine(addresses) } } -extension BoltzSwap: Codable {} +extension GetAddressesResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwap: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwap { +public struct FfiConverterTypeGetAddressesResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAddressesResponse { return - try BoltzSwap( - id: FfiConverterString.read(from: &buf), - swapType: FfiConverterTypeBoltzSwapType.read(from: &buf), - status: FfiConverterTypeBoltzSwapStatus.read(from: &buf), - network: FfiConverterTypeBoltzNetwork.read(from: &buf), - swapIndex: FfiConverterUInt64.read(from: &buf), - amountSat: FfiConverterUInt64.read(from: &buf), - onchainAmountSat: FfiConverterOptionUInt64.read(from: &buf), - invoice: FfiConverterOptionString.read(from: &buf), - lockupAddress: FfiConverterOptionString.read(from: &buf), - onchainAddress: FfiConverterOptionString.read(from: &buf), - timeoutBlockHeight: FfiConverterUInt64.read(from: &buf), - createdAt: FfiConverterUInt64.read(from: &buf), - claimTxId: FfiConverterOptionString.read(from: &buf), - refundTxId: FfiConverterOptionString.read(from: &buf) + try GetAddressesResponse( + addresses: FfiConverterSequenceTypeGetAddressResponse.read(from: &buf) ) } - public static func write(_ value: BoltzSwap, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypeBoltzSwapType.write(value.swapType, into: &buf) - FfiConverterTypeBoltzSwapStatus.write(value.status, into: &buf) - FfiConverterTypeBoltzNetwork.write(value.network, into: &buf) - FfiConverterUInt64.write(value.swapIndex, into: &buf) - FfiConverterUInt64.write(value.amountSat, into: &buf) - FfiConverterOptionUInt64.write(value.onchainAmountSat, into: &buf) - FfiConverterOptionString.write(value.invoice, into: &buf) - FfiConverterOptionString.write(value.lockupAddress, into: &buf) - FfiConverterOptionString.write(value.onchainAddress, into: &buf) - FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) - FfiConverterUInt64.write(value.createdAt, into: &buf) - FfiConverterOptionString.write(value.claimTxId, into: &buf) - FfiConverterOptionString.write(value.refundTxId, into: &buf) + public static func write(_ value: GetAddressesResponse, into buf: inout [UInt8]) { + FfiConverterSequenceTypeGetAddressResponse.write(value.addresses, into: &buf) } } @@ -3529,87 +5343,218 @@ public struct FfiConverterTypeBoltzSwap: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwap_lift(_ buf: RustBuffer) throws -> BoltzSwap { - return try FfiConverterTypeBoltzSwap.lift(buf) +public func FfiConverterTypeGetAddressesResponse_lift(_ buf: RustBuffer) throws -> GetAddressesResponse { + return try FfiConverterTypeGetAddressesResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwap_lower(_ value: BoltzSwap) -> RustBuffer { - return FfiConverterTypeBoltzSwap.lower(value) +public func FfiConverterTypeGetAddressesResponse_lower(_ value: GetAddressesResponse) -> RustBuffer { + return FfiConverterTypeGetAddressesResponse.lower(value) } -public struct ChannelLiquidityOptions { - public var defaultLspBalanceSat: UInt64 - public var minLspBalanceSat: UInt64 - public var maxLspBalanceSat: UInt64 - public var maxClientBalanceSat: UInt64 +/** + * A single transaction in the wallet's history. + */ +public struct HistoryTransaction { + /** + * Transaction ID (hex) + */ + public var txid: String + /** + * Amount received by the wallet (sats) + */ + public var received: UInt64 + /** + * Amount sent by the wallet (sats) — includes change sent back to self + */ + public var sent: UInt64 + /** + * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) + */ + public var net: Int64 + /** + * Transaction fee in sats (None if not available, e.g. for received-only txs) + */ + public var fee: UInt64? + /** + * Fee rate in sats per virtual byte (None if fee or tx size is unavailable). + */ + public var feeRate: Double? + /** + * Display amount in sats: + * - Received: the received value + * - Sent: amount that left the wallet (sent - received - fee) + * - SelfTransfer: the fee paid + */ + public var amount: UInt64 + /** + * Transaction direction + */ + public var direction: TxDirection + /** + * Block height (None if unconfirmed/mempool) + */ + public var blockHeight: UInt32? + /** + * Block timestamp as unix epoch seconds (None if unconfirmed) + */ + public var timestamp: UInt64? + /** + * Number of confirmations (0 if unconfirmed) + */ + public var confirmations: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(defaultLspBalanceSat: UInt64, minLspBalanceSat: UInt64, maxLspBalanceSat: UInt64, maxClientBalanceSat: UInt64) { - self.defaultLspBalanceSat = defaultLspBalanceSat - self.minLspBalanceSat = minLspBalanceSat - self.maxLspBalanceSat = maxLspBalanceSat - self.maxClientBalanceSat = maxClientBalanceSat + public init( + /** + * Transaction ID (hex) + */txid: String, + /** + * Amount received by the wallet (sats) + */received: UInt64, + /** + * Amount sent by the wallet (sats) — includes change sent back to self + */sent: UInt64, + /** + * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) + */net: Int64, + /** + * Transaction fee in sats (None if not available, e.g. for received-only txs) + */fee: UInt64?, + /** + * Fee rate in sats per virtual byte (None if fee or tx size is unavailable). + */feeRate: Double?, + /** + * Display amount in sats: + * - Received: the received value + * - Sent: amount that left the wallet (sent - received - fee) + * - SelfTransfer: the fee paid + */amount: UInt64, + /** + * Transaction direction + */direction: TxDirection, + /** + * Block height (None if unconfirmed/mempool) + */blockHeight: UInt32?, + /** + * Block timestamp as unix epoch seconds (None if unconfirmed) + */timestamp: UInt64?, + /** + * Number of confirmations (0 if unconfirmed) + */confirmations: UInt32) { + self.txid = txid + self.received = received + self.sent = sent + self.net = net + self.fee = fee + self.feeRate = feeRate + self.amount = amount + self.direction = direction + self.blockHeight = blockHeight + self.timestamp = timestamp + self.confirmations = confirmations } } #if compiler(>=6) -extension ChannelLiquidityOptions: Sendable {} +extension HistoryTransaction: Sendable {} #endif -extension ChannelLiquidityOptions: Equatable, Hashable { - public static func ==(lhs: ChannelLiquidityOptions, rhs: ChannelLiquidityOptions) -> Bool { - if lhs.defaultLspBalanceSat != rhs.defaultLspBalanceSat { +extension HistoryTransaction: Equatable, Hashable { + public static func ==(lhs: HistoryTransaction, rhs: HistoryTransaction) -> Bool { + if lhs.txid != rhs.txid { return false } - if lhs.minLspBalanceSat != rhs.minLspBalanceSat { + if lhs.received != rhs.received { return false } - if lhs.maxLspBalanceSat != rhs.maxLspBalanceSat { + if lhs.sent != rhs.sent { return false } - if lhs.maxClientBalanceSat != rhs.maxClientBalanceSat { + if lhs.net != rhs.net { + return false + } + if lhs.fee != rhs.fee { + return false + } + if lhs.feeRate != rhs.feeRate { + return false + } + if lhs.amount != rhs.amount { + return false + } + if lhs.direction != rhs.direction { + return false + } + if lhs.blockHeight != rhs.blockHeight { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.confirmations != rhs.confirmations { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(defaultLspBalanceSat) - hasher.combine(minLspBalanceSat) - hasher.combine(maxLspBalanceSat) - hasher.combine(maxClientBalanceSat) + hasher.combine(txid) + hasher.combine(received) + hasher.combine(sent) + hasher.combine(net) + hasher.combine(fee) + hasher.combine(feeRate) + hasher.combine(amount) + hasher.combine(direction) + hasher.combine(blockHeight) + hasher.combine(timestamp) + hasher.combine(confirmations) } } -extension ChannelLiquidityOptions: Codable {} +extension HistoryTransaction: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeChannelLiquidityOptions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ChannelLiquidityOptions { +public struct FfiConverterTypeHistoryTransaction: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HistoryTransaction { return - try ChannelLiquidityOptions( - defaultLspBalanceSat: FfiConverterUInt64.read(from: &buf), - minLspBalanceSat: FfiConverterUInt64.read(from: &buf), - maxLspBalanceSat: FfiConverterUInt64.read(from: &buf), - maxClientBalanceSat: FfiConverterUInt64.read(from: &buf) + try HistoryTransaction( + txid: FfiConverterString.read(from: &buf), + received: FfiConverterUInt64.read(from: &buf), + sent: FfiConverterUInt64.read(from: &buf), + net: FfiConverterInt64.read(from: &buf), + fee: FfiConverterOptionUInt64.read(from: &buf), + feeRate: FfiConverterOptionDouble.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + direction: FfiConverterTypeTxDirection.read(from: &buf), + blockHeight: FfiConverterOptionUInt32.read(from: &buf), + timestamp: FfiConverterOptionUInt64.read(from: &buf), + confirmations: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: ChannelLiquidityOptions, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.defaultLspBalanceSat, into: &buf) - FfiConverterUInt64.write(value.minLspBalanceSat, into: &buf) - FfiConverterUInt64.write(value.maxLspBalanceSat, into: &buf) - FfiConverterUInt64.write(value.maxClientBalanceSat, into: &buf) + public static func write(_ value: HistoryTransaction, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt64.write(value.received, into: &buf) + FfiConverterUInt64.write(value.sent, into: &buf) + FfiConverterInt64.write(value.net, into: &buf) + FfiConverterOptionUInt64.write(value.fee, into: &buf) + FfiConverterOptionDouble.write(value.feeRate, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterTypeTxDirection.write(value.direction, into: &buf) + FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) + FfiConverterOptionUInt64.write(value.timestamp, into: &buf) + FfiConverterUInt32.write(value.confirmations, into: &buf) } } @@ -3617,95 +5562,71 @@ public struct FfiConverterTypeChannelLiquidityOptions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeChannelLiquidityOptions_lift(_ buf: RustBuffer) throws -> ChannelLiquidityOptions { - return try FfiConverterTypeChannelLiquidityOptions.lift(buf) +public func FfiConverterTypeHistoryTransaction_lift(_ buf: RustBuffer) throws -> HistoryTransaction { + return try FfiConverterTypeHistoryTransaction.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeChannelLiquidityOptions_lower(_ value: ChannelLiquidityOptions) -> RustBuffer { - return FfiConverterTypeChannelLiquidityOptions.lower(value) +public func FfiConverterTypeHistoryTransaction_lower(_ value: HistoryTransaction) -> RustBuffer { + return FfiConverterTypeHistoryTransaction.lower(value) } -public struct ChannelLiquidityParams { - public var clientBalanceSat: UInt64 - public var existingChannelsTotalSat: UInt64 - public var minChannelSizeSat: UInt64 - public var maxChannelSizeSat: UInt64 - public var satsPerEur: UInt64 +public struct IBt0ConfMinTxFeeWindow { + public var satPerVbyte: Double + public var validityEndsAt: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(clientBalanceSat: UInt64, existingChannelsTotalSat: UInt64, minChannelSizeSat: UInt64, maxChannelSizeSat: UInt64, satsPerEur: UInt64) { - self.clientBalanceSat = clientBalanceSat - self.existingChannelsTotalSat = existingChannelsTotalSat - self.minChannelSizeSat = minChannelSizeSat - self.maxChannelSizeSat = maxChannelSizeSat - self.satsPerEur = satsPerEur + public init(satPerVbyte: Double, validityEndsAt: String) { + self.satPerVbyte = satPerVbyte + self.validityEndsAt = validityEndsAt } } #if compiler(>=6) -extension ChannelLiquidityParams: Sendable {} +extension IBt0ConfMinTxFeeWindow: Sendable {} #endif -extension ChannelLiquidityParams: Equatable, Hashable { - public static func ==(lhs: ChannelLiquidityParams, rhs: ChannelLiquidityParams) -> Bool { - if lhs.clientBalanceSat != rhs.clientBalanceSat { - return false - } - if lhs.existingChannelsTotalSat != rhs.existingChannelsTotalSat { - return false - } - if lhs.minChannelSizeSat != rhs.minChannelSizeSat { - return false - } - if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { +extension IBt0ConfMinTxFeeWindow: Equatable, Hashable { + public static func ==(lhs: IBt0ConfMinTxFeeWindow, rhs: IBt0ConfMinTxFeeWindow) -> Bool { + if lhs.satPerVbyte != rhs.satPerVbyte { return false } - if lhs.satsPerEur != rhs.satsPerEur { + if lhs.validityEndsAt != rhs.validityEndsAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(clientBalanceSat) - hasher.combine(existingChannelsTotalSat) - hasher.combine(minChannelSizeSat) - hasher.combine(maxChannelSizeSat) - hasher.combine(satsPerEur) + hasher.combine(satPerVbyte) + hasher.combine(validityEndsAt) } } -extension ChannelLiquidityParams: Codable {} +extension IBt0ConfMinTxFeeWindow: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeChannelLiquidityParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ChannelLiquidityParams { +public struct FfiConverterTypeIBt0ConfMinTxFeeWindow: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBt0ConfMinTxFeeWindow { return - try ChannelLiquidityParams( - clientBalanceSat: FfiConverterUInt64.read(from: &buf), - existingChannelsTotalSat: FfiConverterUInt64.read(from: &buf), - minChannelSizeSat: FfiConverterUInt64.read(from: &buf), - maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), - satsPerEur: FfiConverterUInt64.read(from: &buf) + try IBt0ConfMinTxFeeWindow( + satPerVbyte: FfiConverterDouble.read(from: &buf), + validityEndsAt: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: ChannelLiquidityParams, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) - FfiConverterUInt64.write(value.existingChannelsTotalSat, into: &buf) - FfiConverterUInt64.write(value.minChannelSizeSat, into: &buf) - FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) - FfiConverterUInt64.write(value.satsPerEur, into: &buf) + public static func write(_ value: IBt0ConfMinTxFeeWindow, into buf: inout [UInt8]) { + FfiConverterDouble.write(value.satPerVbyte, into: &buf) + FfiConverterString.write(value.validityEndsAt, into: &buf) } } @@ -3713,167 +5634,87 @@ public struct FfiConverterTypeChannelLiquidityParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeChannelLiquidityParams_lift(_ buf: RustBuffer) throws -> ChannelLiquidityParams { - return try FfiConverterTypeChannelLiquidityParams.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeChannelLiquidityParams_lower(_ value: ChannelLiquidityParams) -> RustBuffer { - return FfiConverterTypeChannelLiquidityParams.lower(value) +public func FfiConverterTypeIBt0ConfMinTxFeeWindow_lift(_ buf: RustBuffer) throws -> IBt0ConfMinTxFeeWindow { + return try FfiConverterTypeIBt0ConfMinTxFeeWindow.lift(buf) } - -public struct ClosedChannelDetails { - public var channelId: String - public var counterpartyNodeId: String - public var fundingTxoTxid: String - public var fundingTxoIndex: UInt32 - public var channelValueSats: UInt64 - public var closedAt: UInt64 - public var outboundCapacityMsat: UInt64 - public var inboundCapacityMsat: UInt64 - public var counterpartyUnspendablePunishmentReserve: UInt64 - public var unspendablePunishmentReserve: UInt64 - public var forwardingFeeProportionalMillionths: UInt32 - public var forwardingFeeBaseMsat: UInt32 - public var channelName: String - public var channelClosureReason: String +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeIBt0ConfMinTxFeeWindow_lower(_ value: IBt0ConfMinTxFeeWindow) -> RustBuffer { + return FfiConverterTypeIBt0ConfMinTxFeeWindow.lower(value) +} + + +public struct IBtBolt11Invoice { + public var request: String + public var state: BtBolt11InvoiceState + public var expiresAt: String + public var updatedAt: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(channelId: String, counterpartyNodeId: String, fundingTxoTxid: String, fundingTxoIndex: UInt32, channelValueSats: UInt64, closedAt: UInt64, outboundCapacityMsat: UInt64, inboundCapacityMsat: UInt64, counterpartyUnspendablePunishmentReserve: UInt64, unspendablePunishmentReserve: UInt64, forwardingFeeProportionalMillionths: UInt32, forwardingFeeBaseMsat: UInt32, channelName: String, channelClosureReason: String) { - self.channelId = channelId - self.counterpartyNodeId = counterpartyNodeId - self.fundingTxoTxid = fundingTxoTxid - self.fundingTxoIndex = fundingTxoIndex - self.channelValueSats = channelValueSats - self.closedAt = closedAt - self.outboundCapacityMsat = outboundCapacityMsat - self.inboundCapacityMsat = inboundCapacityMsat - self.counterpartyUnspendablePunishmentReserve = counterpartyUnspendablePunishmentReserve - self.unspendablePunishmentReserve = unspendablePunishmentReserve - self.forwardingFeeProportionalMillionths = forwardingFeeProportionalMillionths - self.forwardingFeeBaseMsat = forwardingFeeBaseMsat - self.channelName = channelName - self.channelClosureReason = channelClosureReason + public init(request: String, state: BtBolt11InvoiceState, expiresAt: String, updatedAt: String) { + self.request = request + self.state = state + self.expiresAt = expiresAt + self.updatedAt = updatedAt } } #if compiler(>=6) -extension ClosedChannelDetails: Sendable {} +extension IBtBolt11Invoice: Sendable {} #endif -extension ClosedChannelDetails: Equatable, Hashable { - public static func ==(lhs: ClosedChannelDetails, rhs: ClosedChannelDetails) -> Bool { - if lhs.channelId != rhs.channelId { - return false - } - if lhs.counterpartyNodeId != rhs.counterpartyNodeId { - return false - } - if lhs.fundingTxoTxid != rhs.fundingTxoTxid { - return false - } - if lhs.fundingTxoIndex != rhs.fundingTxoIndex { - return false - } - if lhs.channelValueSats != rhs.channelValueSats { - return false - } - if lhs.closedAt != rhs.closedAt { - return false - } - if lhs.outboundCapacityMsat != rhs.outboundCapacityMsat { - return false - } - if lhs.inboundCapacityMsat != rhs.inboundCapacityMsat { - return false - } - if lhs.counterpartyUnspendablePunishmentReserve != rhs.counterpartyUnspendablePunishmentReserve { - return false - } - if lhs.unspendablePunishmentReserve != rhs.unspendablePunishmentReserve { - return false - } - if lhs.forwardingFeeProportionalMillionths != rhs.forwardingFeeProportionalMillionths { +extension IBtBolt11Invoice: Equatable, Hashable { + public static func ==(lhs: IBtBolt11Invoice, rhs: IBtBolt11Invoice) -> Bool { + if lhs.request != rhs.request { return false } - if lhs.forwardingFeeBaseMsat != rhs.forwardingFeeBaseMsat { + if lhs.state != rhs.state { return false } - if lhs.channelName != rhs.channelName { + if lhs.expiresAt != rhs.expiresAt { return false } - if lhs.channelClosureReason != rhs.channelClosureReason { + if lhs.updatedAt != rhs.updatedAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(channelId) - hasher.combine(counterpartyNodeId) - hasher.combine(fundingTxoTxid) - hasher.combine(fundingTxoIndex) - hasher.combine(channelValueSats) - hasher.combine(closedAt) - hasher.combine(outboundCapacityMsat) - hasher.combine(inboundCapacityMsat) - hasher.combine(counterpartyUnspendablePunishmentReserve) - hasher.combine(unspendablePunishmentReserve) - hasher.combine(forwardingFeeProportionalMillionths) - hasher.combine(forwardingFeeBaseMsat) - hasher.combine(channelName) - hasher.combine(channelClosureReason) + hasher.combine(request) + hasher.combine(state) + hasher.combine(expiresAt) + hasher.combine(updatedAt) } } -extension ClosedChannelDetails: Codable {} +extension IBtBolt11Invoice: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeClosedChannelDetails: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ClosedChannelDetails { +public struct FfiConverterTypeIBtBolt11Invoice: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtBolt11Invoice { return - try ClosedChannelDetails( - channelId: FfiConverterString.read(from: &buf), - counterpartyNodeId: FfiConverterString.read(from: &buf), - fundingTxoTxid: FfiConverterString.read(from: &buf), - fundingTxoIndex: FfiConverterUInt32.read(from: &buf), - channelValueSats: FfiConverterUInt64.read(from: &buf), - closedAt: FfiConverterUInt64.read(from: &buf), - outboundCapacityMsat: FfiConverterUInt64.read(from: &buf), - inboundCapacityMsat: FfiConverterUInt64.read(from: &buf), - counterpartyUnspendablePunishmentReserve: FfiConverterUInt64.read(from: &buf), - unspendablePunishmentReserve: FfiConverterUInt64.read(from: &buf), - forwardingFeeProportionalMillionths: FfiConverterUInt32.read(from: &buf), - forwardingFeeBaseMsat: FfiConverterUInt32.read(from: &buf), - channelName: FfiConverterString.read(from: &buf), - channelClosureReason: FfiConverterString.read(from: &buf) + try IBtBolt11Invoice( + request: FfiConverterString.read(from: &buf), + state: FfiConverterTypeBtBolt11InvoiceState.read(from: &buf), + expiresAt: FfiConverterString.read(from: &buf), + updatedAt: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: ClosedChannelDetails, into buf: inout [UInt8]) { - FfiConverterString.write(value.channelId, into: &buf) - FfiConverterString.write(value.counterpartyNodeId, into: &buf) - FfiConverterString.write(value.fundingTxoTxid, into: &buf) - FfiConverterUInt32.write(value.fundingTxoIndex, into: &buf) - FfiConverterUInt64.write(value.channelValueSats, into: &buf) - FfiConverterUInt64.write(value.closedAt, into: &buf) - FfiConverterUInt64.write(value.outboundCapacityMsat, into: &buf) - FfiConverterUInt64.write(value.inboundCapacityMsat, into: &buf) - FfiConverterUInt64.write(value.counterpartyUnspendablePunishmentReserve, into: &buf) - FfiConverterUInt64.write(value.unspendablePunishmentReserve, into: &buf) - FfiConverterUInt32.write(value.forwardingFeeProportionalMillionths, into: &buf) - FfiConverterUInt32.write(value.forwardingFeeBaseMsat, into: &buf) - FfiConverterString.write(value.channelName, into: &buf) - FfiConverterString.write(value.channelClosureReason, into: &buf) + public static func write(_ value: IBtBolt11Invoice, into buf: inout [UInt8]) { + FfiConverterString.write(value.request, into: &buf) + FfiConverterTypeBtBolt11InvoiceState.write(value.state, into: &buf) + FfiConverterString.write(value.expiresAt, into: &buf) + FfiConverterString.write(value.updatedAt, into: &buf) } } @@ -3881,86 +5722,119 @@ public struct FfiConverterTypeClosedChannelDetails: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeClosedChannelDetails_lift(_ buf: RustBuffer) throws -> ClosedChannelDetails { - return try FfiConverterTypeClosedChannelDetails.lift(buf) +public func FfiConverterTypeIBtBolt11Invoice_lift(_ buf: RustBuffer) throws -> IBtBolt11Invoice { + return try FfiConverterTypeIBtBolt11Invoice.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeClosedChannelDetails_lower(_ value: ClosedChannelDetails) -> RustBuffer { - return FfiConverterTypeClosedChannelDetails.lower(value) +public func FfiConverterTypeIBtBolt11Invoice_lower(_ value: IBtBolt11Invoice) -> RustBuffer { + return FfiConverterTypeIBtBolt11Invoice.lower(value) } -/** - * A finalized transaction ready to broadcast. - */ -public struct CompletedTransaction { - /** - * Consensus-encoded transaction hex. - */ - public var serializedTx: String - /** - * Canonical transaction id, excluding witness data. - */ - public var txid: String +public struct IBtChannel { + public var state: BtOpenChannelState + public var lspNodePubkey: String + public var clientNodePubkey: String + public var announceChannel: Bool + public var fundingTx: FundingTx + public var closingTxId: String? + public var close: IBtChannelClose? + public var shortChannelId: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Consensus-encoded transaction hex. - */serializedTx: String, - /** - * Canonical transaction id, excluding witness data. - */txid: String) { - self.serializedTx = serializedTx - self.txid = txid + public init(state: BtOpenChannelState, lspNodePubkey: String, clientNodePubkey: String, announceChannel: Bool, fundingTx: FundingTx, closingTxId: String?, close: IBtChannelClose?, shortChannelId: String?) { + self.state = state + self.lspNodePubkey = lspNodePubkey + self.clientNodePubkey = clientNodePubkey + self.announceChannel = announceChannel + self.fundingTx = fundingTx + self.closingTxId = closingTxId + self.close = close + self.shortChannelId = shortChannelId } } #if compiler(>=6) -extension CompletedTransaction: Sendable {} +extension IBtChannel: Sendable {} #endif -extension CompletedTransaction: Equatable, Hashable { - public static func ==(lhs: CompletedTransaction, rhs: CompletedTransaction) -> Bool { - if lhs.serializedTx != rhs.serializedTx { +extension IBtChannel: Equatable, Hashable { + public static func ==(lhs: IBtChannel, rhs: IBtChannel) -> Bool { + if lhs.state != rhs.state { return false } - if lhs.txid != rhs.txid { + if lhs.lspNodePubkey != rhs.lspNodePubkey { + return false + } + if lhs.clientNodePubkey != rhs.clientNodePubkey { + return false + } + if lhs.announceChannel != rhs.announceChannel { + return false + } + if lhs.fundingTx != rhs.fundingTx { + return false + } + if lhs.closingTxId != rhs.closingTxId { + return false + } + if lhs.close != rhs.close { + return false + } + if lhs.shortChannelId != rhs.shortChannelId { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(serializedTx) - hasher.combine(txid) + hasher.combine(state) + hasher.combine(lspNodePubkey) + hasher.combine(clientNodePubkey) + hasher.combine(announceChannel) + hasher.combine(fundingTx) + hasher.combine(closingTxId) + hasher.combine(close) + hasher.combine(shortChannelId) } } -extension CompletedTransaction: Codable {} +extension IBtChannel: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCompletedTransaction: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CompletedTransaction { +public struct FfiConverterTypeIBtChannel: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtChannel { return - try CompletedTransaction( - serializedTx: FfiConverterString.read(from: &buf), - txid: FfiConverterString.read(from: &buf) + try IBtChannel( + state: FfiConverterTypeBtOpenChannelState.read(from: &buf), + lspNodePubkey: FfiConverterString.read(from: &buf), + clientNodePubkey: FfiConverterString.read(from: &buf), + announceChannel: FfiConverterBool.read(from: &buf), + fundingTx: FfiConverterTypeFundingTx.read(from: &buf), + closingTxId: FfiConverterOptionString.read(from: &buf), + close: FfiConverterOptionTypeIBtChannelClose.read(from: &buf), + shortChannelId: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: CompletedTransaction, into buf: inout [UInt8]) { - FfiConverterString.write(value.serializedTx, into: &buf) - FfiConverterString.write(value.txid, into: &buf) + public static func write(_ value: IBtChannel, into buf: inout [UInt8]) { + FfiConverterTypeBtOpenChannelState.write(value.state, into: &buf) + FfiConverterString.write(value.lspNodePubkey, into: &buf) + FfiConverterString.write(value.clientNodePubkey, into: &buf) + FfiConverterBool.write(value.announceChannel, into: &buf) + FfiConverterTypeFundingTx.write(value.fundingTx, into: &buf) + FfiConverterOptionString.write(value.closingTxId, into: &buf) + FfiConverterOptionTypeIBtChannelClose.write(value.close, into: &buf) + FfiConverterOptionString.write(value.shortChannelId, into: &buf) } } @@ -3968,100 +5842,87 @@ public struct FfiConverterTypeCompletedTransaction: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCompletedTransaction_lift(_ buf: RustBuffer) throws -> CompletedTransaction { - return try FfiConverterTypeCompletedTransaction.lift(buf) +public func FfiConverterTypeIBtChannel_lift(_ buf: RustBuffer) throws -> IBtChannel { + return try FfiConverterTypeIBtChannel.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCompletedTransaction_lower(_ value: CompletedTransaction) -> RustBuffer { - return FfiConverterTypeCompletedTransaction.lower(value) +public func FfiConverterTypeIBtChannel_lower(_ value: IBtChannel) -> RustBuffer { + return FfiConverterTypeIBtChannel.lower(value) } -/** - * Full account structure with addresses and UTXOs. - */ -public struct ComposeAccount { - /** - * Account derivation path (e.g., "m/84'/0'/0'") - */ - public var path: String - /** - * Categorized addresses - */ - public var addresses: AccountAddresses - /** - * Unspent transaction outputs - */ - public var utxo: [AccountUtxo] +public struct IBtChannelClose { + public var txId: String + public var closeType: String + public var initiator: String + public var registeredAt: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Account derivation path (e.g., "m/84'/0'/0'") - */path: String, - /** - * Categorized addresses - */addresses: AccountAddresses, - /** - * Unspent transaction outputs - */utxo: [AccountUtxo]) { - self.path = path - self.addresses = addresses - self.utxo = utxo + public init(txId: String, closeType: String, initiator: String, registeredAt: String) { + self.txId = txId + self.closeType = closeType + self.initiator = initiator + self.registeredAt = registeredAt } } #if compiler(>=6) -extension ComposeAccount: Sendable {} +extension IBtChannelClose: Sendable {} #endif -extension ComposeAccount: Equatable, Hashable { - public static func ==(lhs: ComposeAccount, rhs: ComposeAccount) -> Bool { - if lhs.path != rhs.path { +extension IBtChannelClose: Equatable, Hashable { + public static func ==(lhs: IBtChannelClose, rhs: IBtChannelClose) -> Bool { + if lhs.txId != rhs.txId { return false } - if lhs.addresses != rhs.addresses { + if lhs.closeType != rhs.closeType { return false } - if lhs.utxo != rhs.utxo { + if lhs.initiator != rhs.initiator { + return false + } + if lhs.registeredAt != rhs.registeredAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(addresses) - hasher.combine(utxo) + hasher.combine(txId) + hasher.combine(closeType) + hasher.combine(initiator) + hasher.combine(registeredAt) } } -extension ComposeAccount: Codable {} +extension IBtChannelClose: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeAccount: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeAccount { +public struct FfiConverterTypeIBtChannelClose: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtChannelClose { return - try ComposeAccount( - path: FfiConverterString.read(from: &buf), - addresses: FfiConverterTypeAccountAddresses.read(from: &buf), - utxo: FfiConverterSequenceTypeAccountUtxo.read(from: &buf) + try IBtChannelClose( + txId: FfiConverterString.read(from: &buf), + closeType: FfiConverterString.read(from: &buf), + initiator: FfiConverterString.read(from: &buf), + registeredAt: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: ComposeAccount, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterTypeAccountAddresses.write(value.addresses, into: &buf) - FfiConverterSequenceTypeAccountUtxo.write(value.utxo, into: &buf) + public static func write(_ value: IBtChannelClose, into buf: inout [UInt8]) { + FfiConverterString.write(value.txId, into: &buf) + FfiConverterString.write(value.closeType, into: &buf) + FfiConverterString.write(value.initiator, into: &buf) + FfiConverterString.write(value.registeredAt, into: &buf) } } @@ -4069,114 +5930,71 @@ public struct FfiConverterTypeComposeAccount: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeAccount_lift(_ buf: RustBuffer) throws -> ComposeAccount { - return try FfiConverterTypeComposeAccount.lift(buf) +public func FfiConverterTypeIBtChannelClose_lift(_ buf: RustBuffer) throws -> IBtChannelClose { + return try FfiConverterTypeIBtChannelClose.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeAccount_lower(_ value: ComposeAccount) -> RustBuffer { - return FfiConverterTypeComposeAccount.lower(value) +public func FfiConverterTypeIBtChannelClose_lower(_ value: IBtChannelClose) -> RustBuffer { + return FfiConverterTypeIBtChannelClose.lower(value) } -/** - * Parameters for composing a signer-agnostic transaction. - */ -public struct ComposeParams { - /** - * Wallet configuration (key, server, network) - */ - public var wallet: WalletParams - /** - * Desired transaction outputs - */ - public var outputs: [ComposeOutput] - /** - * Fee rates to evaluate (sat/vB), one PSBT per rate - */ - public var feeRates: [Float] - /** - * UTXO selection strategy (defaults to BranchAndBound) - */ - public var coinSelection: CoinSelection? +public struct IBtEstimateFeeResponse { + public var feeSat: UInt64 + public var min0ConfTxFee: IBt0ConfMinTxFeeWindow // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Wallet configuration (key, server, network) - */wallet: WalletParams, - /** - * Desired transaction outputs - */outputs: [ComposeOutput], - /** - * Fee rates to evaluate (sat/vB), one PSBT per rate - */feeRates: [Float], - /** - * UTXO selection strategy (defaults to BranchAndBound) - */coinSelection: CoinSelection?) { - self.wallet = wallet - self.outputs = outputs - self.feeRates = feeRates - self.coinSelection = coinSelection + public init(feeSat: UInt64, min0ConfTxFee: IBt0ConfMinTxFeeWindow) { + self.feeSat = feeSat + self.min0ConfTxFee = min0ConfTxFee } } #if compiler(>=6) -extension ComposeParams: Sendable {} +extension IBtEstimateFeeResponse: Sendable {} #endif -extension ComposeParams: Equatable, Hashable { - public static func ==(lhs: ComposeParams, rhs: ComposeParams) -> Bool { - if lhs.wallet != rhs.wallet { - return false - } - if lhs.outputs != rhs.outputs { - return false - } - if lhs.feeRates != rhs.feeRates { +extension IBtEstimateFeeResponse: Equatable, Hashable { + public static func ==(lhs: IBtEstimateFeeResponse, rhs: IBtEstimateFeeResponse) -> Bool { + if lhs.feeSat != rhs.feeSat { return false } - if lhs.coinSelection != rhs.coinSelection { + if lhs.min0ConfTxFee != rhs.min0ConfTxFee { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(wallet) - hasher.combine(outputs) - hasher.combine(feeRates) - hasher.combine(coinSelection) + hasher.combine(feeSat) + hasher.combine(min0ConfTxFee) } } -extension ComposeParams: Codable {} +extension IBtEstimateFeeResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeParams { +public struct FfiConverterTypeIBtEstimateFeeResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtEstimateFeeResponse { return - try ComposeParams( - wallet: FfiConverterTypeWalletParams.read(from: &buf), - outputs: FfiConverterSequenceTypeComposeOutput.read(from: &buf), - feeRates: FfiConverterSequenceFloat.read(from: &buf), - coinSelection: FfiConverterOptionTypeCoinSelection.read(from: &buf) + try IBtEstimateFeeResponse( + feeSat: FfiConverterUInt64.read(from: &buf), + min0ConfTxFee: FfiConverterTypeIBt0ConfMinTxFeeWindow.read(from: &buf) ) } - public static func write(_ value: ComposeParams, into buf: inout [UInt8]) { - FfiConverterTypeWalletParams.write(value.wallet, into: &buf) - FfiConverterSequenceTypeComposeOutput.write(value.outputs, into: &buf) - FfiConverterSequenceFloat.write(value.feeRates, into: &buf) - FfiConverterOptionTypeCoinSelection.write(value.coinSelection, into: &buf) + public static func write(_ value: IBtEstimateFeeResponse, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.feeSat, into: &buf) + FfiConverterTypeIBt0ConfMinTxFeeWindow.write(value.min0ConfTxFee, into: &buf) } } @@ -4184,71 +6002,87 @@ public struct FfiConverterTypeComposeParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeParams_lift(_ buf: RustBuffer) throws -> ComposeParams { - return try FfiConverterTypeComposeParams.lift(buf) +public func FfiConverterTypeIBtEstimateFeeResponse_lift(_ buf: RustBuffer) throws -> IBtEstimateFeeResponse { + return try FfiConverterTypeIBtEstimateFeeResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeParams_lower(_ value: ComposeParams) -> RustBuffer { - return FfiConverterTypeComposeParams.lower(value) +public func FfiConverterTypeIBtEstimateFeeResponse_lower(_ value: IBtEstimateFeeResponse) -> RustBuffer { + return FfiConverterTypeIBtEstimateFeeResponse.lower(value) } -public struct CreateCjitOptions { - public var source: String? - public var discountCode: String? +public struct IBtEstimateFeeResponse2 { + public var feeSat: UInt64 + public var networkFeeSat: UInt64 + public var serviceFeeSat: UInt64 + public var min0ConfTxFee: IBt0ConfMinTxFeeWindow // Default memberwise initializers are never public by default, so we // declare one manually. - public init(source: String?, discountCode: String?) { - self.source = source - self.discountCode = discountCode + public init(feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, min0ConfTxFee: IBt0ConfMinTxFeeWindow) { + self.feeSat = feeSat + self.networkFeeSat = networkFeeSat + self.serviceFeeSat = serviceFeeSat + self.min0ConfTxFee = min0ConfTxFee } } #if compiler(>=6) -extension CreateCjitOptions: Sendable {} +extension IBtEstimateFeeResponse2: Sendable {} #endif -extension CreateCjitOptions: Equatable, Hashable { - public static func ==(lhs: CreateCjitOptions, rhs: CreateCjitOptions) -> Bool { - if lhs.source != rhs.source { +extension IBtEstimateFeeResponse2: Equatable, Hashable { + public static func ==(lhs: IBtEstimateFeeResponse2, rhs: IBtEstimateFeeResponse2) -> Bool { + if lhs.feeSat != rhs.feeSat { return false } - if lhs.discountCode != rhs.discountCode { + if lhs.networkFeeSat != rhs.networkFeeSat { + return false + } + if lhs.serviceFeeSat != rhs.serviceFeeSat { + return false + } + if lhs.min0ConfTxFee != rhs.min0ConfTxFee { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(source) - hasher.combine(discountCode) + hasher.combine(feeSat) + hasher.combine(networkFeeSat) + hasher.combine(serviceFeeSat) + hasher.combine(min0ConfTxFee) } } -extension CreateCjitOptions: Codable {} +extension IBtEstimateFeeResponse2: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCreateCjitOptions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateCjitOptions { +public struct FfiConverterTypeIBtEstimateFeeResponse2: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtEstimateFeeResponse2 { return - try CreateCjitOptions( - source: FfiConverterOptionString.read(from: &buf), - discountCode: FfiConverterOptionString.read(from: &buf) + try IBtEstimateFeeResponse2( + feeSat: FfiConverterUInt64.read(from: &buf), + networkFeeSat: FfiConverterUInt64.read(from: &buf), + serviceFeeSat: FfiConverterUInt64.read(from: &buf), + min0ConfTxFee: FfiConverterTypeIBt0ConfMinTxFeeWindow.read(from: &buf) ) } - public static func write(_ value: CreateCjitOptions, into buf: inout [UInt8]) { - FfiConverterOptionString.write(value.source, into: &buf) - FfiConverterOptionString.write(value.discountCode, into: &buf) + public static func write(_ value: IBtEstimateFeeResponse2, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.feeSat, into: &buf) + FfiConverterUInt64.write(value.networkFeeSat, into: &buf) + FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) + FfiConverterTypeIBt0ConfMinTxFeeWindow.write(value.min0ConfTxFee, into: &buf) } } @@ -4256,159 +6090,95 @@ public struct FfiConverterTypeCreateCjitOptions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateCjitOptions_lift(_ buf: RustBuffer) throws -> CreateCjitOptions { - return try FfiConverterTypeCreateCjitOptions.lift(buf) +public func FfiConverterTypeIBtEstimateFeeResponse2_lift(_ buf: RustBuffer) throws -> IBtEstimateFeeResponse2 { + return try FfiConverterTypeIBtEstimateFeeResponse2.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateCjitOptions_lower(_ value: CreateCjitOptions) -> RustBuffer { - return FfiConverterTypeCreateCjitOptions.lower(value) +public func FfiConverterTypeIBtEstimateFeeResponse2_lower(_ value: IBtEstimateFeeResponse2) -> RustBuffer { + return FfiConverterTypeIBtEstimateFeeResponse2.lower(value) } -public struct CreateOrderOptions { - public var clientBalanceSat: UInt64 - public var lspNodeId: String? - public var couponCode: String - public var source: String? - public var discountCode: String? - public var zeroConf: Bool - public var zeroConfPayment: Bool? - public var zeroReserve: Bool - public var clientNodeId: String? - public var signature: String? - public var timestamp: String? - public var refundOnchainAddress: String? - public var announceChannel: Bool +public struct IBtInfo { + public var version: UInt32 + public var nodes: [ILspNode] + public var options: IBtInfoOptions + public var versions: IBtInfoVersions + public var onchain: IBtInfoOnchain // Default memberwise initializers are never public by default, so we // declare one manually. - public init(clientBalanceSat: UInt64, lspNodeId: String?, couponCode: String, source: String?, discountCode: String?, zeroConf: Bool, zeroConfPayment: Bool?, zeroReserve: Bool, clientNodeId: String?, signature: String?, timestamp: String?, refundOnchainAddress: String?, announceChannel: Bool) { - self.clientBalanceSat = clientBalanceSat - self.lspNodeId = lspNodeId - self.couponCode = couponCode - self.source = source - self.discountCode = discountCode - self.zeroConf = zeroConf - self.zeroConfPayment = zeroConfPayment - self.zeroReserve = zeroReserve - self.clientNodeId = clientNodeId - self.signature = signature - self.timestamp = timestamp - self.refundOnchainAddress = refundOnchainAddress - self.announceChannel = announceChannel + public init(version: UInt32, nodes: [ILspNode], options: IBtInfoOptions, versions: IBtInfoVersions, onchain: IBtInfoOnchain) { + self.version = version + self.nodes = nodes + self.options = options + self.versions = versions + self.onchain = onchain } } #if compiler(>=6) -extension CreateOrderOptions: Sendable {} +extension IBtInfo: Sendable {} #endif -extension CreateOrderOptions: Equatable, Hashable { - public static func ==(lhs: CreateOrderOptions, rhs: CreateOrderOptions) -> Bool { - if lhs.clientBalanceSat != rhs.clientBalanceSat { - return false - } - if lhs.lspNodeId != rhs.lspNodeId { - return false - } - if lhs.couponCode != rhs.couponCode { - return false - } - if lhs.source != rhs.source { - return false - } - if lhs.discountCode != rhs.discountCode { - return false - } - if lhs.zeroConf != rhs.zeroConf { - return false - } - if lhs.zeroConfPayment != rhs.zeroConfPayment { - return false - } - if lhs.zeroReserve != rhs.zeroReserve { - return false - } - if lhs.clientNodeId != rhs.clientNodeId { +extension IBtInfo: Equatable, Hashable { + public static func ==(lhs: IBtInfo, rhs: IBtInfo) -> Bool { + if lhs.version != rhs.version { return false } - if lhs.signature != rhs.signature { + if lhs.nodes != rhs.nodes { return false } - if lhs.timestamp != rhs.timestamp { + if lhs.options != rhs.options { return false } - if lhs.refundOnchainAddress != rhs.refundOnchainAddress { + if lhs.versions != rhs.versions { return false } - if lhs.announceChannel != rhs.announceChannel { + if lhs.onchain != rhs.onchain { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(clientBalanceSat) - hasher.combine(lspNodeId) - hasher.combine(couponCode) - hasher.combine(source) - hasher.combine(discountCode) - hasher.combine(zeroConf) - hasher.combine(zeroConfPayment) - hasher.combine(zeroReserve) - hasher.combine(clientNodeId) - hasher.combine(signature) - hasher.combine(timestamp) - hasher.combine(refundOnchainAddress) - hasher.combine(announceChannel) + hasher.combine(version) + hasher.combine(nodes) + hasher.combine(options) + hasher.combine(versions) + hasher.combine(onchain) } } -extension CreateOrderOptions: Codable {} +extension IBtInfo: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCreateOrderOptions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CreateOrderOptions { +public struct FfiConverterTypeIBtInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfo { return - try CreateOrderOptions( - clientBalanceSat: FfiConverterUInt64.read(from: &buf), - lspNodeId: FfiConverterOptionString.read(from: &buf), - couponCode: FfiConverterString.read(from: &buf), - source: FfiConverterOptionString.read(from: &buf), - discountCode: FfiConverterOptionString.read(from: &buf), - zeroConf: FfiConverterBool.read(from: &buf), - zeroConfPayment: FfiConverterOptionBool.read(from: &buf), - zeroReserve: FfiConverterBool.read(from: &buf), - clientNodeId: FfiConverterOptionString.read(from: &buf), - signature: FfiConverterOptionString.read(from: &buf), - timestamp: FfiConverterOptionString.read(from: &buf), - refundOnchainAddress: FfiConverterOptionString.read(from: &buf), - announceChannel: FfiConverterBool.read(from: &buf) + try IBtInfo( + version: FfiConverterUInt32.read(from: &buf), + nodes: FfiConverterSequenceTypeILspNode.read(from: &buf), + options: FfiConverterTypeIBtInfoOptions.read(from: &buf), + versions: FfiConverterTypeIBtInfoVersions.read(from: &buf), + onchain: FfiConverterTypeIBtInfoOnchain.read(from: &buf) ) } - public static func write(_ value: CreateOrderOptions, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) - FfiConverterOptionString.write(value.lspNodeId, into: &buf) - FfiConverterString.write(value.couponCode, into: &buf) - FfiConverterOptionString.write(value.source, into: &buf) - FfiConverterOptionString.write(value.discountCode, into: &buf) - FfiConverterBool.write(value.zeroConf, into: &buf) - FfiConverterOptionBool.write(value.zeroConfPayment, into: &buf) - FfiConverterBool.write(value.zeroReserve, into: &buf) - FfiConverterOptionString.write(value.clientNodeId, into: &buf) - FfiConverterOptionString.write(value.signature, into: &buf) - FfiConverterOptionString.write(value.timestamp, into: &buf) - FfiConverterOptionString.write(value.refundOnchainAddress, into: &buf) - FfiConverterBool.write(value.announceChannel, into: &buf) + public static func write(_ value: IBtInfo, into buf: inout [UInt8]) { + FfiConverterUInt32.write(value.version, into: &buf) + FfiConverterSequenceTypeILspNode.write(value.nodes, into: &buf) + FfiConverterTypeIBtInfoOptions.write(value.options, into: &buf) + FfiConverterTypeIBtInfoVersions.write(value.versions, into: &buf) + FfiConverterTypeIBtInfoOnchain.write(value.onchain, into: &buf) } } @@ -4416,79 +6186,71 @@ public struct FfiConverterTypeCreateOrderOptions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateOrderOptions_lift(_ buf: RustBuffer) throws -> CreateOrderOptions { - return try FfiConverterTypeCreateOrderOptions.lift(buf) +public func FfiConverterTypeIBtInfo_lift(_ buf: RustBuffer) throws -> IBtInfo { + return try FfiConverterTypeIBtInfo.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCreateOrderOptions_lower(_ value: CreateOrderOptions) -> RustBuffer { - return FfiConverterTypeCreateOrderOptions.lower(value) +public func FfiConverterTypeIBtInfo_lower(_ value: IBtInfo) -> RustBuffer { + return FfiConverterTypeIBtInfo.lower(value) } -public struct DefaultLspBalanceParams { - public var clientBalanceSat: UInt64 - public var maxChannelSizeSat: UInt64 - public var satsPerEur: UInt64 +public struct IBtInfoOnchain { + public var network: BitcoinNetworkEnum + public var feeRates: FeeRates // Default memberwise initializers are never public by default, so we // declare one manually. - public init(clientBalanceSat: UInt64, maxChannelSizeSat: UInt64, satsPerEur: UInt64) { - self.clientBalanceSat = clientBalanceSat - self.maxChannelSizeSat = maxChannelSizeSat - self.satsPerEur = satsPerEur + public init(network: BitcoinNetworkEnum, feeRates: FeeRates) { + self.network = network + self.feeRates = feeRates } } #if compiler(>=6) -extension DefaultLspBalanceParams: Sendable {} +extension IBtInfoOnchain: Sendable {} #endif -extension DefaultLspBalanceParams: Equatable, Hashable { - public static func ==(lhs: DefaultLspBalanceParams, rhs: DefaultLspBalanceParams) -> Bool { - if lhs.clientBalanceSat != rhs.clientBalanceSat { - return false - } - if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { +extension IBtInfoOnchain: Equatable, Hashable { + public static func ==(lhs: IBtInfoOnchain, rhs: IBtInfoOnchain) -> Bool { + if lhs.network != rhs.network { return false } - if lhs.satsPerEur != rhs.satsPerEur { + if lhs.feeRates != rhs.feeRates { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(clientBalanceSat) - hasher.combine(maxChannelSizeSat) - hasher.combine(satsPerEur) + hasher.combine(network) + hasher.combine(feeRates) } } -extension DefaultLspBalanceParams: Codable {} +extension IBtInfoOnchain: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeDefaultLspBalanceParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DefaultLspBalanceParams { +public struct FfiConverterTypeIBtInfoOnchain: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoOnchain { return - try DefaultLspBalanceParams( - clientBalanceSat: FfiConverterUInt64.read(from: &buf), - maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), - satsPerEur: FfiConverterUInt64.read(from: &buf) + try IBtInfoOnchain( + network: FfiConverterTypeBitcoinNetworkEnum.read(from: &buf), + feeRates: FfiConverterTypeFeeRates.read(from: &buf) ) } - public static func write(_ value: DefaultLspBalanceParams, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) - FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) - FfiConverterUInt64.write(value.satsPerEur, into: &buf) + public static func write(_ value: IBtInfoOnchain, into buf: inout [UInt8]) { + FfiConverterTypeBitcoinNetworkEnum.write(value.network, into: &buf) + FfiConverterTypeFeeRates.write(value.feeRates, into: &buf) } } @@ -4496,63 +6258,119 @@ public struct FfiConverterTypeDefaultLspBalanceParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDefaultLspBalanceParams_lift(_ buf: RustBuffer) throws -> DefaultLspBalanceParams { - return try FfiConverterTypeDefaultLspBalanceParams.lift(buf) +public func FfiConverterTypeIBtInfoOnchain_lift(_ buf: RustBuffer) throws -> IBtInfoOnchain { + return try FfiConverterTypeIBtInfoOnchain.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDefaultLspBalanceParams_lower(_ value: DefaultLspBalanceParams) -> RustBuffer { - return FfiConverterTypeDefaultLspBalanceParams.lower(value) +public func FfiConverterTypeIBtInfoOnchain_lower(_ value: IBtInfoOnchain) -> RustBuffer { + return FfiConverterTypeIBtInfoOnchain.lower(value) } -public struct ErrorData { - public var errorDetails: String +public struct IBtInfoOptions { + public var minChannelSizeSat: UInt64 + public var maxChannelSizeSat: UInt64 + public var minExpiryWeeks: UInt32 + public var maxExpiryWeeks: UInt32 + public var minPaymentConfirmations: UInt32 + public var minHighRiskPaymentConfirmations: UInt32 + public var max0ConfClientBalanceSat: UInt64 + public var maxClientBalanceSat: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(errorDetails: String) { - self.errorDetails = errorDetails + public init(minChannelSizeSat: UInt64, maxChannelSizeSat: UInt64, minExpiryWeeks: UInt32, maxExpiryWeeks: UInt32, minPaymentConfirmations: UInt32, minHighRiskPaymentConfirmations: UInt32, max0ConfClientBalanceSat: UInt64, maxClientBalanceSat: UInt64) { + self.minChannelSizeSat = minChannelSizeSat + self.maxChannelSizeSat = maxChannelSizeSat + self.minExpiryWeeks = minExpiryWeeks + self.maxExpiryWeeks = maxExpiryWeeks + self.minPaymentConfirmations = minPaymentConfirmations + self.minHighRiskPaymentConfirmations = minHighRiskPaymentConfirmations + self.max0ConfClientBalanceSat = max0ConfClientBalanceSat + self.maxClientBalanceSat = maxClientBalanceSat } } #if compiler(>=6) -extension ErrorData: Sendable {} +extension IBtInfoOptions: Sendable {} #endif -extension ErrorData: Equatable, Hashable { - public static func ==(lhs: ErrorData, rhs: ErrorData) -> Bool { - if lhs.errorDetails != rhs.errorDetails { +extension IBtInfoOptions: Equatable, Hashable { + public static func ==(lhs: IBtInfoOptions, rhs: IBtInfoOptions) -> Bool { + if lhs.minChannelSizeSat != rhs.minChannelSizeSat { + return false + } + if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { + return false + } + if lhs.minExpiryWeeks != rhs.minExpiryWeeks { + return false + } + if lhs.maxExpiryWeeks != rhs.maxExpiryWeeks { + return false + } + if lhs.minPaymentConfirmations != rhs.minPaymentConfirmations { + return false + } + if lhs.minHighRiskPaymentConfirmations != rhs.minHighRiskPaymentConfirmations { + return false + } + if lhs.max0ConfClientBalanceSat != rhs.max0ConfClientBalanceSat { + return false + } + if lhs.maxClientBalanceSat != rhs.maxClientBalanceSat { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(errorDetails) + hasher.combine(minChannelSizeSat) + hasher.combine(maxChannelSizeSat) + hasher.combine(minExpiryWeeks) + hasher.combine(maxExpiryWeeks) + hasher.combine(minPaymentConfirmations) + hasher.combine(minHighRiskPaymentConfirmations) + hasher.combine(max0ConfClientBalanceSat) + hasher.combine(maxClientBalanceSat) } } -extension ErrorData: Codable {} +extension IBtInfoOptions: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeErrorData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ErrorData { +public struct FfiConverterTypeIBtInfoOptions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoOptions { return - try ErrorData( - errorDetails: FfiConverterString.read(from: &buf) + try IBtInfoOptions( + minChannelSizeSat: FfiConverterUInt64.read(from: &buf), + maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), + minExpiryWeeks: FfiConverterUInt32.read(from: &buf), + maxExpiryWeeks: FfiConverterUInt32.read(from: &buf), + minPaymentConfirmations: FfiConverterUInt32.read(from: &buf), + minHighRiskPaymentConfirmations: FfiConverterUInt32.read(from: &buf), + max0ConfClientBalanceSat: FfiConverterUInt64.read(from: &buf), + maxClientBalanceSat: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: ErrorData, into buf: inout [UInt8]) { - FfiConverterString.write(value.errorDetails, into: &buf) + public static func write(_ value: IBtInfoOptions, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.minChannelSizeSat, into: &buf) + FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) + FfiConverterUInt32.write(value.minExpiryWeeks, into: &buf) + FfiConverterUInt32.write(value.maxExpiryWeeks, into: &buf) + FfiConverterUInt32.write(value.minPaymentConfirmations, into: &buf) + FfiConverterUInt32.write(value.minHighRiskPaymentConfirmations, into: &buf) + FfiConverterUInt64.write(value.max0ConfClientBalanceSat, into: &buf) + FfiConverterUInt64.write(value.maxClientBalanceSat, into: &buf) } } @@ -4560,79 +6378,79 @@ public struct FfiConverterTypeErrorData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeErrorData_lift(_ buf: RustBuffer) throws -> ErrorData { - return try FfiConverterTypeErrorData.lift(buf) +public func FfiConverterTypeIBtInfoOptions_lift(_ buf: RustBuffer) throws -> IBtInfoOptions { + return try FfiConverterTypeIBtInfoOptions.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeErrorData_lower(_ value: ErrorData) -> RustBuffer { - return FfiConverterTypeErrorData.lower(value) +public func FfiConverterTypeIBtInfoOptions_lower(_ value: IBtInfoOptions) -> RustBuffer { + return FfiConverterTypeIBtInfoOptions.lower(value) } -public struct FeeRates { - public var fast: UInt32 - public var mid: UInt32 - public var slow: UInt32 +public struct IBtInfoVersions { + public var http: String + public var btc: String + public var ln2: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(fast: UInt32, mid: UInt32, slow: UInt32) { - self.fast = fast - self.mid = mid - self.slow = slow + public init(http: String, btc: String, ln2: String) { + self.http = http + self.btc = btc + self.ln2 = ln2 } } #if compiler(>=6) -extension FeeRates: Sendable {} +extension IBtInfoVersions: Sendable {} #endif -extension FeeRates: Equatable, Hashable { - public static func ==(lhs: FeeRates, rhs: FeeRates) -> Bool { - if lhs.fast != rhs.fast { +extension IBtInfoVersions: Equatable, Hashable { + public static func ==(lhs: IBtInfoVersions, rhs: IBtInfoVersions) -> Bool { + if lhs.http != rhs.http { return false } - if lhs.mid != rhs.mid { + if lhs.btc != rhs.btc { return false } - if lhs.slow != rhs.slow { + if lhs.ln2 != rhs.ln2 { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(fast) - hasher.combine(mid) - hasher.combine(slow) + hasher.combine(http) + hasher.combine(btc) + hasher.combine(ln2) } } -extension FeeRates: Codable {} +extension IBtInfoVersions: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeFeeRates: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FeeRates { +public struct FfiConverterTypeIBtInfoVersions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoVersions { return - try FeeRates( - fast: FfiConverterUInt32.read(from: &buf), - mid: FfiConverterUInt32.read(from: &buf), - slow: FfiConverterUInt32.read(from: &buf) + try IBtInfoVersions( + http: FfiConverterString.read(from: &buf), + btc: FfiConverterString.read(from: &buf), + ln2: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: FeeRates, into buf: inout [UInt8]) { - FfiConverterUInt32.write(value.fast, into: &buf) - FfiConverterUInt32.write(value.mid, into: &buf) - FfiConverterUInt32.write(value.slow, into: &buf) + public static func write(_ value: IBtInfoVersions, into buf: inout [UInt8]) { + FfiConverterString.write(value.http, into: &buf) + FfiConverterString.write(value.btc, into: &buf) + FfiConverterString.write(value.ln2, into: &buf) } } @@ -4640,71 +6458,119 @@ public struct FfiConverterTypeFeeRates: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFeeRates_lift(_ buf: RustBuffer) throws -> FeeRates { - return try FfiConverterTypeFeeRates.lift(buf) +public func FfiConverterTypeIBtInfoVersions_lift(_ buf: RustBuffer) throws -> IBtInfoVersions { + return try FfiConverterTypeIBtInfoVersions.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFeeRates_lower(_ value: FeeRates) -> RustBuffer { - return FfiConverterTypeFeeRates.lower(value) +public func FfiConverterTypeIBtInfoVersions_lower(_ value: IBtInfoVersions) -> RustBuffer { + return FfiConverterTypeIBtInfoVersions.lower(value) } -public struct FundingTx { - public var id: String - public var vout: UInt64 +public struct IBtOnchainTransaction { + public var amountSat: UInt64 + public var txId: String + public var vout: UInt32 + public var blockHeight: UInt32? + public var blockConfirmationCount: UInt32 + public var feeRateSatPerVbyte: Double + public var confirmed: Bool + public var suspicious0ConfReason: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, vout: UInt64) { - self.id = id + public init(amountSat: UInt64, txId: String, vout: UInt32, blockHeight: UInt32?, blockConfirmationCount: UInt32, feeRateSatPerVbyte: Double, confirmed: Bool, suspicious0ConfReason: String) { + self.amountSat = amountSat + self.txId = txId self.vout = vout + self.blockHeight = blockHeight + self.blockConfirmationCount = blockConfirmationCount + self.feeRateSatPerVbyte = feeRateSatPerVbyte + self.confirmed = confirmed + self.suspicious0ConfReason = suspicious0ConfReason } } #if compiler(>=6) -extension FundingTx: Sendable {} +extension IBtOnchainTransaction: Sendable {} #endif -extension FundingTx: Equatable, Hashable { - public static func ==(lhs: FundingTx, rhs: FundingTx) -> Bool { - if lhs.id != rhs.id { +extension IBtOnchainTransaction: Equatable, Hashable { + public static func ==(lhs: IBtOnchainTransaction, rhs: IBtOnchainTransaction) -> Bool { + if lhs.amountSat != rhs.amountSat { + return false + } + if lhs.txId != rhs.txId { return false } if lhs.vout != rhs.vout { return false } + if lhs.blockHeight != rhs.blockHeight { + return false + } + if lhs.blockConfirmationCount != rhs.blockConfirmationCount { + return false + } + if lhs.feeRateSatPerVbyte != rhs.feeRateSatPerVbyte { + return false + } + if lhs.confirmed != rhs.confirmed { + return false + } + if lhs.suspicious0ConfReason != rhs.suspicious0ConfReason { + return false + } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) + hasher.combine(amountSat) + hasher.combine(txId) hasher.combine(vout) + hasher.combine(blockHeight) + hasher.combine(blockConfirmationCount) + hasher.combine(feeRateSatPerVbyte) + hasher.combine(confirmed) + hasher.combine(suspicious0ConfReason) } } -extension FundingTx: Codable {} +extension IBtOnchainTransaction: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeFundingTx: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FundingTx { +public struct FfiConverterTypeIBtOnchainTransaction: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOnchainTransaction { return - try FundingTx( - id: FfiConverterString.read(from: &buf), - vout: FfiConverterUInt64.read(from: &buf) + try IBtOnchainTransaction( + amountSat: FfiConverterUInt64.read(from: &buf), + txId: FfiConverterString.read(from: &buf), + vout: FfiConverterUInt32.read(from: &buf), + blockHeight: FfiConverterOptionUInt32.read(from: &buf), + blockConfirmationCount: FfiConverterUInt32.read(from: &buf), + feeRateSatPerVbyte: FfiConverterDouble.read(from: &buf), + confirmed: FfiConverterBool.read(from: &buf), + suspicious0ConfReason: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: FundingTx, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterUInt64.write(value.vout, into: &buf) + public static func write(_ value: IBtOnchainTransaction, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.amountSat, into: &buf) + FfiConverterString.write(value.txId, into: &buf) + FfiConverterUInt32.write(value.vout, into: &buf) + FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) + FfiConverterUInt32.write(value.blockConfirmationCount, into: &buf) + FfiConverterDouble.write(value.feeRateSatPerVbyte, into: &buf) + FfiConverterBool.write(value.confirmed, into: &buf) + FfiConverterString.write(value.suspicious0ConfReason, into: &buf) } } @@ -4712,64 +6578,51 @@ public struct FfiConverterTypeFundingTx: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFundingTx_lift(_ buf: RustBuffer) throws -> FundingTx { - return try FfiConverterTypeFundingTx.lift(buf) +public func FfiConverterTypeIBtOnchainTransaction_lift(_ buf: RustBuffer) throws -> IBtOnchainTransaction { + return try FfiConverterTypeIBtOnchainTransaction.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeFundingTx_lower(_ value: FundingTx) -> RustBuffer { - return FfiConverterTypeFundingTx.lower(value) +public func FfiConverterTypeIBtOnchainTransaction_lower(_ value: IBtOnchainTransaction) -> RustBuffer { + return FfiConverterTypeIBtOnchainTransaction.lower(value) } -public struct GetAddressResponse { - /** - * The generated Bitcoin address as a string - */ +public struct IBtOnchainTransactions { public var address: String - /** - * The derivation path used to generate the address - */ - public var path: String - /** - * The hexadecimal representation of the public key - */ - public var publicKey: String + public var confirmedSat: UInt64 + public var requiredConfirmations: UInt32 + public var transactions: [IBtOnchainTransaction] // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * The generated Bitcoin address as a string - */address: String, - /** - * The derivation path used to generate the address - */path: String, - /** - * The hexadecimal representation of the public key - */publicKey: String) { + public init(address: String, confirmedSat: UInt64, requiredConfirmations: UInt32, transactions: [IBtOnchainTransaction]) { self.address = address - self.path = path - self.publicKey = publicKey + self.confirmedSat = confirmedSat + self.requiredConfirmations = requiredConfirmations + self.transactions = transactions } } #if compiler(>=6) -extension GetAddressResponse: Sendable {} +extension IBtOnchainTransactions: Sendable {} #endif -extension GetAddressResponse: Equatable, Hashable { - public static func ==(lhs: GetAddressResponse, rhs: GetAddressResponse) -> Bool { +extension IBtOnchainTransactions: Equatable, Hashable { + public static func ==(lhs: IBtOnchainTransactions, rhs: IBtOnchainTransactions) -> Bool { if lhs.address != rhs.address { return false } - if lhs.path != rhs.path { + if lhs.confirmedSat != rhs.confirmedSat { return false } - if lhs.publicKey != rhs.publicKey { + if lhs.requiredConfirmations != rhs.requiredConfirmations { + return false + } + if lhs.transactions != rhs.transactions { return false } return true @@ -4777,32 +6630,35 @@ extension GetAddressResponse: Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(address) - hasher.combine(path) - hasher.combine(publicKey) + hasher.combine(confirmedSat) + hasher.combine(requiredConfirmations) + hasher.combine(transactions) } } -extension GetAddressResponse: Codable {} +extension IBtOnchainTransactions: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeGetAddressResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAddressResponse { +public struct FfiConverterTypeIBtOnchainTransactions: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOnchainTransactions { return - try GetAddressResponse( + try IBtOnchainTransactions( address: FfiConverterString.read(from: &buf), - path: FfiConverterString.read(from: &buf), - publicKey: FfiConverterString.read(from: &buf) + confirmedSat: FfiConverterUInt64.read(from: &buf), + requiredConfirmations: FfiConverterUInt32.read(from: &buf), + transactions: FfiConverterSequenceTypeIBtOnchainTransaction.read(from: &buf) ) } - public static func write(_ value: GetAddressResponse, into buf: inout [UInt8]) { + public static func write(_ value: IBtOnchainTransactions, into buf: inout [UInt8]) { FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.path, into: &buf) - FfiConverterString.write(value.publicKey, into: &buf) + FfiConverterUInt64.write(value.confirmedSat, into: &buf) + FfiConverterUInt32.write(value.requiredConfirmations, into: &buf) + FfiConverterSequenceTypeIBtOnchainTransaction.write(value.transactions, into: &buf) } } @@ -4810,69 +6666,239 @@ public struct FfiConverterTypeGetAddressResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAddressResponse_lift(_ buf: RustBuffer) throws -> GetAddressResponse { - return try FfiConverterTypeGetAddressResponse.lift(buf) +public func FfiConverterTypeIBtOnchainTransactions_lift(_ buf: RustBuffer) throws -> IBtOnchainTransactions { + return try FfiConverterTypeIBtOnchainTransactions.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAddressResponse_lower(_ value: GetAddressResponse) -> RustBuffer { - return FfiConverterTypeGetAddressResponse.lower(value) +public func FfiConverterTypeIBtOnchainTransactions_lower(_ value: IBtOnchainTransactions) -> RustBuffer { + return FfiConverterTypeIBtOnchainTransactions.lower(value) } -public struct GetAddressesResponse { - /** - * Vector of generated Bitcoin addresses - */ - public var addresses: [GetAddressResponse] +public struct IBtOrder { + public var id: String + public var state: BtOrderState + public var state2: BtOrderState2? + public var feeSat: UInt64 + public var networkFeeSat: UInt64 + public var serviceFeeSat: UInt64 + public var lspBalanceSat: UInt64 + public var clientBalanceSat: UInt64 + public var zeroConf: Bool + public var zeroReserve: Bool + public var clientNodeId: String? + public var channelExpiryWeeks: UInt32 + public var channelExpiresAt: String + public var orderExpiresAt: String + public var channel: IBtChannel? + public var lspNode: ILspNode? + public var lnurl: String? + public var payment: IBtPayment? + public var couponCode: String? + public var source: String? + public var discount: IDiscount? + public var updatedAt: String + public var createdAt: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Vector of generated Bitcoin addresses - */addresses: [GetAddressResponse]) { - self.addresses = addresses + public init(id: String, state: BtOrderState, state2: BtOrderState2?, feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, lspBalanceSat: UInt64, clientBalanceSat: UInt64, zeroConf: Bool, zeroReserve: Bool, clientNodeId: String?, channelExpiryWeeks: UInt32, channelExpiresAt: String, orderExpiresAt: String, channel: IBtChannel?, lspNode: ILspNode?, lnurl: String?, payment: IBtPayment?, couponCode: String?, source: String?, discount: IDiscount?, updatedAt: String, createdAt: String) { + self.id = id + self.state = state + self.state2 = state2 + self.feeSat = feeSat + self.networkFeeSat = networkFeeSat + self.serviceFeeSat = serviceFeeSat + self.lspBalanceSat = lspBalanceSat + self.clientBalanceSat = clientBalanceSat + self.zeroConf = zeroConf + self.zeroReserve = zeroReserve + self.clientNodeId = clientNodeId + self.channelExpiryWeeks = channelExpiryWeeks + self.channelExpiresAt = channelExpiresAt + self.orderExpiresAt = orderExpiresAt + self.channel = channel + self.lspNode = lspNode + self.lnurl = lnurl + self.payment = payment + self.couponCode = couponCode + self.source = source + self.discount = discount + self.updatedAt = updatedAt + self.createdAt = createdAt } } #if compiler(>=6) -extension GetAddressesResponse: Sendable {} +extension IBtOrder: Sendable {} #endif -extension GetAddressesResponse: Equatable, Hashable { - public static func ==(lhs: GetAddressesResponse, rhs: GetAddressesResponse) -> Bool { - if lhs.addresses != rhs.addresses { +extension IBtOrder: Equatable, Hashable { + public static func ==(lhs: IBtOrder, rhs: IBtOrder) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.state != rhs.state { + return false + } + if lhs.state2 != rhs.state2 { + return false + } + if lhs.feeSat != rhs.feeSat { + return false + } + if lhs.networkFeeSat != rhs.networkFeeSat { + return false + } + if lhs.serviceFeeSat != rhs.serviceFeeSat { + return false + } + if lhs.lspBalanceSat != rhs.lspBalanceSat { + return false + } + if lhs.clientBalanceSat != rhs.clientBalanceSat { + return false + } + if lhs.zeroConf != rhs.zeroConf { + return false + } + if lhs.zeroReserve != rhs.zeroReserve { + return false + } + if lhs.clientNodeId != rhs.clientNodeId { + return false + } + if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { + return false + } + if lhs.channelExpiresAt != rhs.channelExpiresAt { + return false + } + if lhs.orderExpiresAt != rhs.orderExpiresAt { + return false + } + if lhs.channel != rhs.channel { + return false + } + if lhs.lspNode != rhs.lspNode { + return false + } + if lhs.lnurl != rhs.lnurl { + return false + } + if lhs.payment != rhs.payment { + return false + } + if lhs.couponCode != rhs.couponCode { + return false + } + if lhs.source != rhs.source { + return false + } + if lhs.discount != rhs.discount { + return false + } + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.createdAt != rhs.createdAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(addresses) + hasher.combine(id) + hasher.combine(state) + hasher.combine(state2) + hasher.combine(feeSat) + hasher.combine(networkFeeSat) + hasher.combine(serviceFeeSat) + hasher.combine(lspBalanceSat) + hasher.combine(clientBalanceSat) + hasher.combine(zeroConf) + hasher.combine(zeroReserve) + hasher.combine(clientNodeId) + hasher.combine(channelExpiryWeeks) + hasher.combine(channelExpiresAt) + hasher.combine(orderExpiresAt) + hasher.combine(channel) + hasher.combine(lspNode) + hasher.combine(lnurl) + hasher.combine(payment) + hasher.combine(couponCode) + hasher.combine(source) + hasher.combine(discount) + hasher.combine(updatedAt) + hasher.combine(createdAt) } } -extension GetAddressesResponse: Codable {} +extension IBtOrder: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeGetAddressesResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> GetAddressesResponse { +public struct FfiConverterTypeIBtOrder: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOrder { return - try GetAddressesResponse( - addresses: FfiConverterSequenceTypeGetAddressResponse.read(from: &buf) + try IBtOrder( + id: FfiConverterString.read(from: &buf), + state: FfiConverterTypeBtOrderState.read(from: &buf), + state2: FfiConverterOptionTypeBtOrderState2.read(from: &buf), + feeSat: FfiConverterUInt64.read(from: &buf), + networkFeeSat: FfiConverterUInt64.read(from: &buf), + serviceFeeSat: FfiConverterUInt64.read(from: &buf), + lspBalanceSat: FfiConverterUInt64.read(from: &buf), + clientBalanceSat: FfiConverterUInt64.read(from: &buf), + zeroConf: FfiConverterBool.read(from: &buf), + zeroReserve: FfiConverterBool.read(from: &buf), + clientNodeId: FfiConverterOptionString.read(from: &buf), + channelExpiryWeeks: FfiConverterUInt32.read(from: &buf), + channelExpiresAt: FfiConverterString.read(from: &buf), + orderExpiresAt: FfiConverterString.read(from: &buf), + channel: FfiConverterOptionTypeIBtChannel.read(from: &buf), + lspNode: FfiConverterOptionTypeILspNode.read(from: &buf), + lnurl: FfiConverterOptionString.read(from: &buf), + payment: FfiConverterOptionTypeIBtPayment.read(from: &buf), + couponCode: FfiConverterOptionString.read(from: &buf), + source: FfiConverterOptionString.read(from: &buf), + discount: FfiConverterOptionTypeIDiscount.read(from: &buf), + updatedAt: FfiConverterString.read(from: &buf), + createdAt: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: GetAddressesResponse, into buf: inout [UInt8]) { - FfiConverterSequenceTypeGetAddressResponse.write(value.addresses, into: &buf) + public static func write(_ value: IBtOrder, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypeBtOrderState.write(value.state, into: &buf) + FfiConverterOptionTypeBtOrderState2.write(value.state2, into: &buf) + FfiConverterUInt64.write(value.feeSat, into: &buf) + FfiConverterUInt64.write(value.networkFeeSat, into: &buf) + FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) + FfiConverterUInt64.write(value.lspBalanceSat, into: &buf) + FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) + FfiConverterBool.write(value.zeroConf, into: &buf) + FfiConverterBool.write(value.zeroReserve, into: &buf) + FfiConverterOptionString.write(value.clientNodeId, into: &buf) + FfiConverterUInt32.write(value.channelExpiryWeeks, into: &buf) + FfiConverterString.write(value.channelExpiresAt, into: &buf) + FfiConverterString.write(value.orderExpiresAt, into: &buf) + FfiConverterOptionTypeIBtChannel.write(value.channel, into: &buf) + FfiConverterOptionTypeILspNode.write(value.lspNode, into: &buf) + FfiConverterOptionString.write(value.lnurl, into: &buf) + FfiConverterOptionTypeIBtPayment.write(value.payment, into: &buf) + FfiConverterOptionString.write(value.couponCode, into: &buf) + FfiConverterOptionString.write(value.source, into: &buf) + FfiConverterOptionTypeIDiscount.write(value.discount, into: &buf) + FfiConverterString.write(value.updatedAt, into: &buf) + FfiConverterString.write(value.createdAt, into: &buf) } } @@ -4880,218 +6906,111 @@ public struct FfiConverterTypeGetAddressesResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAddressesResponse_lift(_ buf: RustBuffer) throws -> GetAddressesResponse { - return try FfiConverterTypeGetAddressesResponse.lift(buf) +public func FfiConverterTypeIBtOrder_lift(_ buf: RustBuffer) throws -> IBtOrder { + return try FfiConverterTypeIBtOrder.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeGetAddressesResponse_lower(_ value: GetAddressesResponse) -> RustBuffer { - return FfiConverterTypeGetAddressesResponse.lower(value) +public func FfiConverterTypeIBtOrder_lower(_ value: IBtOrder) -> RustBuffer { + return FfiConverterTypeIBtOrder.lower(value) } -/** - * A single transaction in the wallet's history. - */ -public struct HistoryTransaction { - /** - * Transaction ID (hex) - */ - public var txid: String - /** - * Amount received by the wallet (sats) - */ - public var received: UInt64 - /** - * Amount sent by the wallet (sats) — includes change sent back to self - */ - public var sent: UInt64 - /** - * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) - */ - public var net: Int64 - /** - * Transaction fee in sats (None if not available, e.g. for received-only txs) - */ - public var fee: UInt64? - /** - * Fee rate in sats per virtual byte (None if fee or tx size is unavailable). - */ - public var feeRate: Double? - /** - * Display amount in sats: - * - Received: the received value - * - Sent: amount that left the wallet (sent - received - fee) - * - SelfTransfer: the fee paid - */ - public var amount: UInt64 - /** - * Transaction direction - */ - public var direction: TxDirection - /** - * Block height (None if unconfirmed/mempool) - */ - public var blockHeight: UInt32? - /** - * Block timestamp as unix epoch seconds (None if unconfirmed) - */ - public var timestamp: UInt64? - /** - * Number of confirmations (0 if unconfirmed) - */ - public var confirmations: UInt32 +public struct IBtPayment { + public var state: BtPaymentState + public var state2: BtPaymentState2? + public var paidSat: UInt64 + public var bolt11Invoice: IBtBolt11Invoice? + public var onchain: IBtOnchainTransactions? + public var isManuallyPaid: Bool? + public var manualRefunds: [IManualRefund]? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Transaction ID (hex) - */txid: String, - /** - * Amount received by the wallet (sats) - */received: UInt64, - /** - * Amount sent by the wallet (sats) — includes change sent back to self - */sent: UInt64, - /** - * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) - */net: Int64, - /** - * Transaction fee in sats (None if not available, e.g. for received-only txs) - */fee: UInt64?, - /** - * Fee rate in sats per virtual byte (None if fee or tx size is unavailable). - */feeRate: Double?, - /** - * Display amount in sats: - * - Received: the received value - * - Sent: amount that left the wallet (sent - received - fee) - * - SelfTransfer: the fee paid - */amount: UInt64, - /** - * Transaction direction - */direction: TxDirection, - /** - * Block height (None if unconfirmed/mempool) - */blockHeight: UInt32?, - /** - * Block timestamp as unix epoch seconds (None if unconfirmed) - */timestamp: UInt64?, - /** - * Number of confirmations (0 if unconfirmed) - */confirmations: UInt32) { - self.txid = txid - self.received = received - self.sent = sent - self.net = net - self.fee = fee - self.feeRate = feeRate - self.amount = amount - self.direction = direction - self.blockHeight = blockHeight - self.timestamp = timestamp - self.confirmations = confirmations + public init(state: BtPaymentState, state2: BtPaymentState2?, paidSat: UInt64, bolt11Invoice: IBtBolt11Invoice?, onchain: IBtOnchainTransactions?, isManuallyPaid: Bool?, manualRefunds: [IManualRefund]?) { + self.state = state + self.state2 = state2 + self.paidSat = paidSat + self.bolt11Invoice = bolt11Invoice + self.onchain = onchain + self.isManuallyPaid = isManuallyPaid + self.manualRefunds = manualRefunds } } #if compiler(>=6) -extension HistoryTransaction: Sendable {} +extension IBtPayment: Sendable {} #endif -extension HistoryTransaction: Equatable, Hashable { - public static func ==(lhs: HistoryTransaction, rhs: HistoryTransaction) -> Bool { - if lhs.txid != rhs.txid { - return false - } - if lhs.received != rhs.received { - return false - } - if lhs.sent != rhs.sent { - return false - } - if lhs.net != rhs.net { - return false - } - if lhs.fee != rhs.fee { +extension IBtPayment: Equatable, Hashable { + public static func ==(lhs: IBtPayment, rhs: IBtPayment) -> Bool { + if lhs.state != rhs.state { return false } - if lhs.feeRate != rhs.feeRate { + if lhs.state2 != rhs.state2 { return false } - if lhs.amount != rhs.amount { + if lhs.paidSat != rhs.paidSat { return false } - if lhs.direction != rhs.direction { + if lhs.bolt11Invoice != rhs.bolt11Invoice { return false } - if lhs.blockHeight != rhs.blockHeight { + if lhs.onchain != rhs.onchain { return false } - if lhs.timestamp != rhs.timestamp { + if lhs.isManuallyPaid != rhs.isManuallyPaid { return false } - if lhs.confirmations != rhs.confirmations { + if lhs.manualRefunds != rhs.manualRefunds { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(received) - hasher.combine(sent) - hasher.combine(net) - hasher.combine(fee) - hasher.combine(feeRate) - hasher.combine(amount) - hasher.combine(direction) - hasher.combine(blockHeight) - hasher.combine(timestamp) - hasher.combine(confirmations) + hasher.combine(state) + hasher.combine(state2) + hasher.combine(paidSat) + hasher.combine(bolt11Invoice) + hasher.combine(onchain) + hasher.combine(isManuallyPaid) + hasher.combine(manualRefunds) } } -extension HistoryTransaction: Codable {} +extension IBtPayment: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeHistoryTransaction: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HistoryTransaction { +public struct FfiConverterTypeIBtPayment: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtPayment { return - try HistoryTransaction( - txid: FfiConverterString.read(from: &buf), - received: FfiConverterUInt64.read(from: &buf), - sent: FfiConverterUInt64.read(from: &buf), - net: FfiConverterInt64.read(from: &buf), - fee: FfiConverterOptionUInt64.read(from: &buf), - feeRate: FfiConverterOptionDouble.read(from: &buf), - amount: FfiConverterUInt64.read(from: &buf), - direction: FfiConverterTypeTxDirection.read(from: &buf), - blockHeight: FfiConverterOptionUInt32.read(from: &buf), - timestamp: FfiConverterOptionUInt64.read(from: &buf), - confirmations: FfiConverterUInt32.read(from: &buf) + try IBtPayment( + state: FfiConverterTypeBtPaymentState.read(from: &buf), + state2: FfiConverterOptionTypeBtPaymentState2.read(from: &buf), + paidSat: FfiConverterUInt64.read(from: &buf), + bolt11Invoice: FfiConverterOptionTypeIBtBolt11Invoice.read(from: &buf), + onchain: FfiConverterOptionTypeIBtOnchainTransactions.read(from: &buf), + isManuallyPaid: FfiConverterOptionBool.read(from: &buf), + manualRefunds: FfiConverterOptionSequenceTypeIManualRefund.read(from: &buf) ) } - public static func write(_ value: HistoryTransaction, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt64.write(value.received, into: &buf) - FfiConverterUInt64.write(value.sent, into: &buf) - FfiConverterInt64.write(value.net, into: &buf) - FfiConverterOptionUInt64.write(value.fee, into: &buf) - FfiConverterOptionDouble.write(value.feeRate, into: &buf) - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterTypeTxDirection.write(value.direction, into: &buf) - FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) - FfiConverterOptionUInt64.write(value.timestamp, into: &buf) - FfiConverterUInt32.write(value.confirmations, into: &buf) + public static func write(_ value: IBtPayment, into buf: inout [UInt8]) { + FfiConverterTypeBtPaymentState.write(value.state, into: &buf) + FfiConverterOptionTypeBtPaymentState2.write(value.state2, into: &buf) + FfiConverterUInt64.write(value.paidSat, into: &buf) + FfiConverterOptionTypeIBtBolt11Invoice.write(value.bolt11Invoice, into: &buf) + FfiConverterOptionTypeIBtOnchainTransactions.write(value.onchain, into: &buf) + FfiConverterOptionBool.write(value.isManuallyPaid, into: &buf) + FfiConverterOptionSequenceTypeIManualRefund.write(value.manualRefunds, into: &buf) } } @@ -5099,71 +7018,199 @@ public struct FfiConverterTypeHistoryTransaction: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHistoryTransaction_lift(_ buf: RustBuffer) throws -> HistoryTransaction { - return try FfiConverterTypeHistoryTransaction.lift(buf) +public func FfiConverterTypeIBtPayment_lift(_ buf: RustBuffer) throws -> IBtPayment { + return try FfiConverterTypeIBtPayment.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHistoryTransaction_lower(_ value: HistoryTransaction) -> RustBuffer { - return FfiConverterTypeHistoryTransaction.lower(value) +public func FfiConverterTypeIBtPayment_lower(_ value: IBtPayment) -> RustBuffer { + return FfiConverterTypeIBtPayment.lower(value) } -public struct IBt0ConfMinTxFeeWindow { - public var satPerVbyte: Double - public var validityEndsAt: String +public struct IcJitEntry { + public var id: String + public var state: CJitStateEnum + public var feeSat: UInt64 + public var networkFeeSat: UInt64 + public var serviceFeeSat: UInt64 + public var channelSizeSat: UInt64 + public var channelExpiryWeeks: UInt32 + public var channelOpenError: String? + public var nodeId: String + public var invoice: IBtBolt11Invoice + public var channel: IBtChannel? + public var lspNode: ILspNode + public var couponCode: String + public var source: String? + public var discount: IDiscount? + public var expiresAt: String + public var updatedAt: String + public var createdAt: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(satPerVbyte: Double, validityEndsAt: String) { - self.satPerVbyte = satPerVbyte - self.validityEndsAt = validityEndsAt + public init(id: String, state: CJitStateEnum, feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, channelSizeSat: UInt64, channelExpiryWeeks: UInt32, channelOpenError: String?, nodeId: String, invoice: IBtBolt11Invoice, channel: IBtChannel?, lspNode: ILspNode, couponCode: String, source: String?, discount: IDiscount?, expiresAt: String, updatedAt: String, createdAt: String) { + self.id = id + self.state = state + self.feeSat = feeSat + self.networkFeeSat = networkFeeSat + self.serviceFeeSat = serviceFeeSat + self.channelSizeSat = channelSizeSat + self.channelExpiryWeeks = channelExpiryWeeks + self.channelOpenError = channelOpenError + self.nodeId = nodeId + self.invoice = invoice + self.channel = channel + self.lspNode = lspNode + self.couponCode = couponCode + self.source = source + self.discount = discount + self.expiresAt = expiresAt + self.updatedAt = updatedAt + self.createdAt = createdAt } } #if compiler(>=6) -extension IBt0ConfMinTxFeeWindow: Sendable {} +extension IcJitEntry: Sendable {} #endif -extension IBt0ConfMinTxFeeWindow: Equatable, Hashable { - public static func ==(lhs: IBt0ConfMinTxFeeWindow, rhs: IBt0ConfMinTxFeeWindow) -> Bool { - if lhs.satPerVbyte != rhs.satPerVbyte { +extension IcJitEntry: Equatable, Hashable { + public static func ==(lhs: IcJitEntry, rhs: IcJitEntry) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.validityEndsAt != rhs.validityEndsAt { + if lhs.state != rhs.state { + return false + } + if lhs.feeSat != rhs.feeSat { + return false + } + if lhs.networkFeeSat != rhs.networkFeeSat { + return false + } + if lhs.serviceFeeSat != rhs.serviceFeeSat { + return false + } + if lhs.channelSizeSat != rhs.channelSizeSat { + return false + } + if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { + return false + } + if lhs.channelOpenError != rhs.channelOpenError { + return false + } + if lhs.nodeId != rhs.nodeId { + return false + } + if lhs.invoice != rhs.invoice { + return false + } + if lhs.channel != rhs.channel { + return false + } + if lhs.lspNode != rhs.lspNode { + return false + } + if lhs.couponCode != rhs.couponCode { + return false + } + if lhs.source != rhs.source { + return false + } + if lhs.discount != rhs.discount { + return false + } + if lhs.expiresAt != rhs.expiresAt { + return false + } + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.createdAt != rhs.createdAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(satPerVbyte) - hasher.combine(validityEndsAt) + hasher.combine(id) + hasher.combine(state) + hasher.combine(feeSat) + hasher.combine(networkFeeSat) + hasher.combine(serviceFeeSat) + hasher.combine(channelSizeSat) + hasher.combine(channelExpiryWeeks) + hasher.combine(channelOpenError) + hasher.combine(nodeId) + hasher.combine(invoice) + hasher.combine(channel) + hasher.combine(lspNode) + hasher.combine(couponCode) + hasher.combine(source) + hasher.combine(discount) + hasher.combine(expiresAt) + hasher.combine(updatedAt) + hasher.combine(createdAt) } } -extension IBt0ConfMinTxFeeWindow: Codable {} +extension IcJitEntry: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBt0ConfMinTxFeeWindow: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBt0ConfMinTxFeeWindow { +public struct FfiConverterTypeICJitEntry: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IcJitEntry { return - try IBt0ConfMinTxFeeWindow( - satPerVbyte: FfiConverterDouble.read(from: &buf), - validityEndsAt: FfiConverterString.read(from: &buf) + try IcJitEntry( + id: FfiConverterString.read(from: &buf), + state: FfiConverterTypeCJitStateEnum.read(from: &buf), + feeSat: FfiConverterUInt64.read(from: &buf), + networkFeeSat: FfiConverterUInt64.read(from: &buf), + serviceFeeSat: FfiConverterUInt64.read(from: &buf), + channelSizeSat: FfiConverterUInt64.read(from: &buf), + channelExpiryWeeks: FfiConverterUInt32.read(from: &buf), + channelOpenError: FfiConverterOptionString.read(from: &buf), + nodeId: FfiConverterString.read(from: &buf), + invoice: FfiConverterTypeIBtBolt11Invoice.read(from: &buf), + channel: FfiConverterOptionTypeIBtChannel.read(from: &buf), + lspNode: FfiConverterTypeILspNode.read(from: &buf), + couponCode: FfiConverterString.read(from: &buf), + source: FfiConverterOptionString.read(from: &buf), + discount: FfiConverterOptionTypeIDiscount.read(from: &buf), + expiresAt: FfiConverterString.read(from: &buf), + updatedAt: FfiConverterString.read(from: &buf), + createdAt: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IBt0ConfMinTxFeeWindow, into buf: inout [UInt8]) { - FfiConverterDouble.write(value.satPerVbyte, into: &buf) - FfiConverterString.write(value.validityEndsAt, into: &buf) + public static func write(_ value: IcJitEntry, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypeCJitStateEnum.write(value.state, into: &buf) + FfiConverterUInt64.write(value.feeSat, into: &buf) + FfiConverterUInt64.write(value.networkFeeSat, into: &buf) + FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) + FfiConverterUInt64.write(value.channelSizeSat, into: &buf) + FfiConverterUInt32.write(value.channelExpiryWeeks, into: &buf) + FfiConverterOptionString.write(value.channelOpenError, into: &buf) + FfiConverterString.write(value.nodeId, into: &buf) + FfiConverterTypeIBtBolt11Invoice.write(value.invoice, into: &buf) + FfiConverterOptionTypeIBtChannel.write(value.channel, into: &buf) + FfiConverterTypeILspNode.write(value.lspNode, into: &buf) + FfiConverterString.write(value.couponCode, into: &buf) + FfiConverterOptionString.write(value.source, into: &buf) + FfiConverterOptionTypeIDiscount.write(value.discount, into: &buf) + FfiConverterString.write(value.expiresAt, into: &buf) + FfiConverterString.write(value.updatedAt, into: &buf) + FfiConverterString.write(value.createdAt, into: &buf) } } @@ -5171,87 +7218,87 @@ public struct FfiConverterTypeIBt0ConfMinTxFeeWindow: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBt0ConfMinTxFeeWindow_lift(_ buf: RustBuffer) throws -> IBt0ConfMinTxFeeWindow { - return try FfiConverterTypeIBt0ConfMinTxFeeWindow.lift(buf) +public func FfiConverterTypeICJitEntry_lift(_ buf: RustBuffer) throws -> IcJitEntry { + return try FfiConverterTypeICJitEntry.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBt0ConfMinTxFeeWindow_lower(_ value: IBt0ConfMinTxFeeWindow) -> RustBuffer { - return FfiConverterTypeIBt0ConfMinTxFeeWindow.lower(value) +public func FfiConverterTypeICJitEntry_lower(_ value: IcJitEntry) -> RustBuffer { + return FfiConverterTypeICJitEntry.lower(value) } -public struct IBtBolt11Invoice { - public var request: String - public var state: BtBolt11InvoiceState - public var expiresAt: String - public var updatedAt: String +public struct IDiscount { + public var code: String + public var absoluteSat: UInt64 + public var relative: Double + public var overallSat: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(request: String, state: BtBolt11InvoiceState, expiresAt: String, updatedAt: String) { - self.request = request - self.state = state - self.expiresAt = expiresAt - self.updatedAt = updatedAt + public init(code: String, absoluteSat: UInt64, relative: Double, overallSat: UInt64) { + self.code = code + self.absoluteSat = absoluteSat + self.relative = relative + self.overallSat = overallSat } } #if compiler(>=6) -extension IBtBolt11Invoice: Sendable {} +extension IDiscount: Sendable {} #endif -extension IBtBolt11Invoice: Equatable, Hashable { - public static func ==(lhs: IBtBolt11Invoice, rhs: IBtBolt11Invoice) -> Bool { - if lhs.request != rhs.request { +extension IDiscount: Equatable, Hashable { + public static func ==(lhs: IDiscount, rhs: IDiscount) -> Bool { + if lhs.code != rhs.code { return false } - if lhs.state != rhs.state { + if lhs.absoluteSat != rhs.absoluteSat { return false } - if lhs.expiresAt != rhs.expiresAt { + if lhs.relative != rhs.relative { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.overallSat != rhs.overallSat { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(request) - hasher.combine(state) - hasher.combine(expiresAt) - hasher.combine(updatedAt) + hasher.combine(code) + hasher.combine(absoluteSat) + hasher.combine(relative) + hasher.combine(overallSat) } } -extension IBtBolt11Invoice: Codable {} +extension IDiscount: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtBolt11Invoice: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtBolt11Invoice { +public struct FfiConverterTypeIDiscount: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IDiscount { return - try IBtBolt11Invoice( - request: FfiConverterString.read(from: &buf), - state: FfiConverterTypeBtBolt11InvoiceState.read(from: &buf), - expiresAt: FfiConverterString.read(from: &buf), - updatedAt: FfiConverterString.read(from: &buf) + try IDiscount( + code: FfiConverterString.read(from: &buf), + absoluteSat: FfiConverterUInt64.read(from: &buf), + relative: FfiConverterDouble.read(from: &buf), + overallSat: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: IBtBolt11Invoice, into buf: inout [UInt8]) { - FfiConverterString.write(value.request, into: &buf) - FfiConverterTypeBtBolt11InvoiceState.write(value.state, into: &buf) - FfiConverterString.write(value.expiresAt, into: &buf) - FfiConverterString.write(value.updatedAt, into: &buf) + public static func write(_ value: IDiscount, into buf: inout [UInt8]) { + FfiConverterString.write(value.code, into: &buf) + FfiConverterUInt64.write(value.absoluteSat, into: &buf) + FfiConverterDouble.write(value.relative, into: &buf) + FfiConverterUInt64.write(value.overallSat, into: &buf) } } @@ -5259,119 +7306,135 @@ public struct FfiConverterTypeIBtBolt11Invoice: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtBolt11Invoice_lift(_ buf: RustBuffer) throws -> IBtBolt11Invoice { - return try FfiConverterTypeIBtBolt11Invoice.lift(buf) +public func FfiConverterTypeIDiscount_lift(_ buf: RustBuffer) throws -> IDiscount { + return try FfiConverterTypeIDiscount.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtBolt11Invoice_lower(_ value: IBtBolt11Invoice) -> RustBuffer { - return FfiConverterTypeIBtBolt11Invoice.lower(value) +public func FfiConverterTypeIDiscount_lower(_ value: IDiscount) -> RustBuffer { + return FfiConverterTypeIDiscount.lower(value) } -public struct IBtChannel { - public var state: BtOpenChannelState - public var lspNodePubkey: String - public var clientNodePubkey: String - public var announceChannel: Bool - public var fundingTx: FundingTx - public var closingTxId: String? - public var close: IBtChannelClose? - public var shortChannelId: String? +public struct IGift { + public var id: String + public var nodeId: String + public var orderId: String? + public var order: IGiftOrder? + public var bolt11PaymentId: String? + public var bolt11Payment: IGiftPayment? + public var appliedGiftCodeId: String? + public var appliedGiftCode: IGiftCode? + public var createdAt: String? + public var updatedAt: String? - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(state: BtOpenChannelState, lspNodePubkey: String, clientNodePubkey: String, announceChannel: Bool, fundingTx: FundingTx, closingTxId: String?, close: IBtChannelClose?, shortChannelId: String?) { - self.state = state - self.lspNodePubkey = lspNodePubkey - self.clientNodePubkey = clientNodePubkey - self.announceChannel = announceChannel - self.fundingTx = fundingTx - self.closingTxId = closingTxId - self.close = close - self.shortChannelId = shortChannelId + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(id: String, nodeId: String, orderId: String?, order: IGiftOrder?, bolt11PaymentId: String?, bolt11Payment: IGiftPayment?, appliedGiftCodeId: String?, appliedGiftCode: IGiftCode?, createdAt: String?, updatedAt: String?) { + self.id = id + self.nodeId = nodeId + self.orderId = orderId + self.order = order + self.bolt11PaymentId = bolt11PaymentId + self.bolt11Payment = bolt11Payment + self.appliedGiftCodeId = appliedGiftCodeId + self.appliedGiftCode = appliedGiftCode + self.createdAt = createdAt + self.updatedAt = updatedAt } } #if compiler(>=6) -extension IBtChannel: Sendable {} +extension IGift: Sendable {} #endif -extension IBtChannel: Equatable, Hashable { - public static func ==(lhs: IBtChannel, rhs: IBtChannel) -> Bool { - if lhs.state != rhs.state { +extension IGift: Equatable, Hashable { + public static func ==(lhs: IGift, rhs: IGift) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.lspNodePubkey != rhs.lspNodePubkey { + if lhs.nodeId != rhs.nodeId { return false } - if lhs.clientNodePubkey != rhs.clientNodePubkey { + if lhs.orderId != rhs.orderId { return false } - if lhs.announceChannel != rhs.announceChannel { + if lhs.order != rhs.order { return false } - if lhs.fundingTx != rhs.fundingTx { + if lhs.bolt11PaymentId != rhs.bolt11PaymentId { return false } - if lhs.closingTxId != rhs.closingTxId { + if lhs.bolt11Payment != rhs.bolt11Payment { return false } - if lhs.close != rhs.close { + if lhs.appliedGiftCodeId != rhs.appliedGiftCodeId { return false } - if lhs.shortChannelId != rhs.shortChannelId { + if lhs.appliedGiftCode != rhs.appliedGiftCode { + return false + } + if lhs.createdAt != rhs.createdAt { + return false + } + if lhs.updatedAt != rhs.updatedAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(state) - hasher.combine(lspNodePubkey) - hasher.combine(clientNodePubkey) - hasher.combine(announceChannel) - hasher.combine(fundingTx) - hasher.combine(closingTxId) - hasher.combine(close) - hasher.combine(shortChannelId) + hasher.combine(id) + hasher.combine(nodeId) + hasher.combine(orderId) + hasher.combine(order) + hasher.combine(bolt11PaymentId) + hasher.combine(bolt11Payment) + hasher.combine(appliedGiftCodeId) + hasher.combine(appliedGiftCode) + hasher.combine(createdAt) + hasher.combine(updatedAt) } } -extension IBtChannel: Codable {} +extension IGift: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtChannel: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtChannel { +public struct FfiConverterTypeIGift: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGift { return - try IBtChannel( - state: FfiConverterTypeBtOpenChannelState.read(from: &buf), - lspNodePubkey: FfiConverterString.read(from: &buf), - clientNodePubkey: FfiConverterString.read(from: &buf), - announceChannel: FfiConverterBool.read(from: &buf), - fundingTx: FfiConverterTypeFundingTx.read(from: &buf), - closingTxId: FfiConverterOptionString.read(from: &buf), - close: FfiConverterOptionTypeIBtChannelClose.read(from: &buf), - shortChannelId: FfiConverterOptionString.read(from: &buf) + try IGift( + id: FfiConverterString.read(from: &buf), + nodeId: FfiConverterString.read(from: &buf), + orderId: FfiConverterOptionString.read(from: &buf), + order: FfiConverterOptionTypeIGiftOrder.read(from: &buf), + bolt11PaymentId: FfiConverterOptionString.read(from: &buf), + bolt11Payment: FfiConverterOptionTypeIGiftPayment.read(from: &buf), + appliedGiftCodeId: FfiConverterOptionString.read(from: &buf), + appliedGiftCode: FfiConverterOptionTypeIGiftCode.read(from: &buf), + createdAt: FfiConverterOptionString.read(from: &buf), + updatedAt: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: IBtChannel, into buf: inout [UInt8]) { - FfiConverterTypeBtOpenChannelState.write(value.state, into: &buf) - FfiConverterString.write(value.lspNodePubkey, into: &buf) - FfiConverterString.write(value.clientNodePubkey, into: &buf) - FfiConverterBool.write(value.announceChannel, into: &buf) - FfiConverterTypeFundingTx.write(value.fundingTx, into: &buf) - FfiConverterOptionString.write(value.closingTxId, into: &buf) - FfiConverterOptionTypeIBtChannelClose.write(value.close, into: &buf) - FfiConverterOptionString.write(value.shortChannelId, into: &buf) + public static func write(_ value: IGift, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.nodeId, into: &buf) + FfiConverterOptionString.write(value.orderId, into: &buf) + FfiConverterOptionTypeIGiftOrder.write(value.order, into: &buf) + FfiConverterOptionString.write(value.bolt11PaymentId, into: &buf) + FfiConverterOptionTypeIGiftPayment.write(value.bolt11Payment, into: &buf) + FfiConverterOptionString.write(value.appliedGiftCodeId, into: &buf) + FfiConverterOptionTypeIGiftCode.write(value.appliedGiftCode, into: &buf) + FfiConverterOptionString.write(value.createdAt, into: &buf) + FfiConverterOptionString.write(value.updatedAt, into: &buf) } } @@ -5379,87 +7442,143 @@ public struct FfiConverterTypeIBtChannel: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtChannel_lift(_ buf: RustBuffer) throws -> IBtChannel { - return try FfiConverterTypeIBtChannel.lift(buf) +public func FfiConverterTypeIGift_lift(_ buf: RustBuffer) throws -> IGift { + return try FfiConverterTypeIGift.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtChannel_lower(_ value: IBtChannel) -> RustBuffer { - return FfiConverterTypeIBtChannel.lower(value) +public func FfiConverterTypeIGift_lower(_ value: IGift) -> RustBuffer { + return FfiConverterTypeIGift.lower(value) } -public struct IBtChannelClose { - public var txId: String - public var closeType: String - public var initiator: String - public var registeredAt: String +public struct IGiftBolt11Invoice { + public var id: String + public var request: String + public var state: String + public var isHodlInvoice: Bool? + public var paymentHash: String? + public var amountSat: UInt64? + public var amountMsat: String? + public var internalNodePubkey: String? + public var updatedAt: String? + public var createdAt: String? + public var expiresAt: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(txId: String, closeType: String, initiator: String, registeredAt: String) { - self.txId = txId - self.closeType = closeType - self.initiator = initiator - self.registeredAt = registeredAt + public init(id: String, request: String, state: String, isHodlInvoice: Bool?, paymentHash: String?, amountSat: UInt64?, amountMsat: String?, internalNodePubkey: String?, updatedAt: String?, createdAt: String?, expiresAt: String?) { + self.id = id + self.request = request + self.state = state + self.isHodlInvoice = isHodlInvoice + self.paymentHash = paymentHash + self.amountSat = amountSat + self.amountMsat = amountMsat + self.internalNodePubkey = internalNodePubkey + self.updatedAt = updatedAt + self.createdAt = createdAt + self.expiresAt = expiresAt } } #if compiler(>=6) -extension IBtChannelClose: Sendable {} +extension IGiftBolt11Invoice: Sendable {} #endif -extension IBtChannelClose: Equatable, Hashable { - public static func ==(lhs: IBtChannelClose, rhs: IBtChannelClose) -> Bool { - if lhs.txId != rhs.txId { +extension IGiftBolt11Invoice: Equatable, Hashable { + public static func ==(lhs: IGiftBolt11Invoice, rhs: IGiftBolt11Invoice) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.closeType != rhs.closeType { + if lhs.request != rhs.request { return false } - if lhs.initiator != rhs.initiator { + if lhs.state != rhs.state { return false } - if lhs.registeredAt != rhs.registeredAt { + if lhs.isHodlInvoice != rhs.isHodlInvoice { + return false + } + if lhs.paymentHash != rhs.paymentHash { + return false + } + if lhs.amountSat != rhs.amountSat { + return false + } + if lhs.amountMsat != rhs.amountMsat { + return false + } + if lhs.internalNodePubkey != rhs.internalNodePubkey { + return false + } + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.createdAt != rhs.createdAt { + return false + } + if lhs.expiresAt != rhs.expiresAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txId) - hasher.combine(closeType) - hasher.combine(initiator) - hasher.combine(registeredAt) + hasher.combine(id) + hasher.combine(request) + hasher.combine(state) + hasher.combine(isHodlInvoice) + hasher.combine(paymentHash) + hasher.combine(amountSat) + hasher.combine(amountMsat) + hasher.combine(internalNodePubkey) + hasher.combine(updatedAt) + hasher.combine(createdAt) + hasher.combine(expiresAt) } } -extension IBtChannelClose: Codable {} +extension IGiftBolt11Invoice: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtChannelClose: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtChannelClose { +public struct FfiConverterTypeIGiftBolt11Invoice: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftBolt11Invoice { return - try IBtChannelClose( - txId: FfiConverterString.read(from: &buf), - closeType: FfiConverterString.read(from: &buf), - initiator: FfiConverterString.read(from: &buf), - registeredAt: FfiConverterString.read(from: &buf) + try IGiftBolt11Invoice( + id: FfiConverterString.read(from: &buf), + request: FfiConverterString.read(from: &buf), + state: FfiConverterString.read(from: &buf), + isHodlInvoice: FfiConverterOptionBool.read(from: &buf), + paymentHash: FfiConverterOptionString.read(from: &buf), + amountSat: FfiConverterOptionUInt64.read(from: &buf), + amountMsat: FfiConverterOptionString.read(from: &buf), + internalNodePubkey: FfiConverterOptionString.read(from: &buf), + updatedAt: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionString.read(from: &buf), + expiresAt: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: IBtChannelClose, into buf: inout [UInt8]) { - FfiConverterString.write(value.txId, into: &buf) - FfiConverterString.write(value.closeType, into: &buf) - FfiConverterString.write(value.initiator, into: &buf) - FfiConverterString.write(value.registeredAt, into: &buf) + public static func write(_ value: IGiftBolt11Invoice, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.request, into: &buf) + FfiConverterString.write(value.state, into: &buf) + FfiConverterOptionBool.write(value.isHodlInvoice, into: &buf) + FfiConverterOptionString.write(value.paymentHash, into: &buf) + FfiConverterOptionUInt64.write(value.amountSat, into: &buf) + FfiConverterOptionString.write(value.amountMsat, into: &buf) + FfiConverterOptionString.write(value.internalNodePubkey, into: &buf) + FfiConverterOptionString.write(value.updatedAt, into: &buf) + FfiConverterOptionString.write(value.createdAt, into: &buf) + FfiConverterOptionString.write(value.expiresAt, into: &buf) } } @@ -5467,71 +7586,127 @@ public struct FfiConverterTypeIBtChannelClose: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtChannelClose_lift(_ buf: RustBuffer) throws -> IBtChannelClose { - return try FfiConverterTypeIBtChannelClose.lift(buf) +public func FfiConverterTypeIGiftBolt11Invoice_lift(_ buf: RustBuffer) throws -> IGiftBolt11Invoice { + return try FfiConverterTypeIGiftBolt11Invoice.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtChannelClose_lower(_ value: IBtChannelClose) -> RustBuffer { - return FfiConverterTypeIBtChannelClose.lower(value) +public func FfiConverterTypeIGiftBolt11Invoice_lower(_ value: IGiftBolt11Invoice) -> RustBuffer { + return FfiConverterTypeIGiftBolt11Invoice.lower(value) } -public struct IBtEstimateFeeResponse { - public var feeSat: UInt64 - public var min0ConfTxFee: IBt0ConfMinTxFeeWindow +public struct IGiftBtcAddress { + public var id: String + public var address: String + public var transactions: [String] + public var allTransactions: [String] + public var isBlacklisted: Bool? + public var watchUntil: String? + public var watchForBlockConfirmations: UInt32? + public var updatedAt: String? + public var createdAt: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(feeSat: UInt64, min0ConfTxFee: IBt0ConfMinTxFeeWindow) { - self.feeSat = feeSat - self.min0ConfTxFee = min0ConfTxFee + public init(id: String, address: String, transactions: [String], allTransactions: [String], isBlacklisted: Bool?, watchUntil: String?, watchForBlockConfirmations: UInt32?, updatedAt: String?, createdAt: String?) { + self.id = id + self.address = address + self.transactions = transactions + self.allTransactions = allTransactions + self.isBlacklisted = isBlacklisted + self.watchUntil = watchUntil + self.watchForBlockConfirmations = watchForBlockConfirmations + self.updatedAt = updatedAt + self.createdAt = createdAt } } #if compiler(>=6) -extension IBtEstimateFeeResponse: Sendable {} +extension IGiftBtcAddress: Sendable {} #endif -extension IBtEstimateFeeResponse: Equatable, Hashable { - public static func ==(lhs: IBtEstimateFeeResponse, rhs: IBtEstimateFeeResponse) -> Bool { - if lhs.feeSat != rhs.feeSat { +extension IGiftBtcAddress: Equatable, Hashable { + public static func ==(lhs: IGiftBtcAddress, rhs: IGiftBtcAddress) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.min0ConfTxFee != rhs.min0ConfTxFee { + if lhs.address != rhs.address { + return false + } + if lhs.transactions != rhs.transactions { + return false + } + if lhs.allTransactions != rhs.allTransactions { + return false + } + if lhs.isBlacklisted != rhs.isBlacklisted { + return false + } + if lhs.watchUntil != rhs.watchUntil { + return false + } + if lhs.watchForBlockConfirmations != rhs.watchForBlockConfirmations { + return false + } + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.createdAt != rhs.createdAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(feeSat) - hasher.combine(min0ConfTxFee) + hasher.combine(id) + hasher.combine(address) + hasher.combine(transactions) + hasher.combine(allTransactions) + hasher.combine(isBlacklisted) + hasher.combine(watchUntil) + hasher.combine(watchForBlockConfirmations) + hasher.combine(updatedAt) + hasher.combine(createdAt) } } -extension IBtEstimateFeeResponse: Codable {} +extension IGiftBtcAddress: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtEstimateFeeResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtEstimateFeeResponse { +public struct FfiConverterTypeIGiftBtcAddress: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftBtcAddress { return - try IBtEstimateFeeResponse( - feeSat: FfiConverterUInt64.read(from: &buf), - min0ConfTxFee: FfiConverterTypeIBt0ConfMinTxFeeWindow.read(from: &buf) + try IGiftBtcAddress( + id: FfiConverterString.read(from: &buf), + address: FfiConverterString.read(from: &buf), + transactions: FfiConverterSequenceString.read(from: &buf), + allTransactions: FfiConverterSequenceString.read(from: &buf), + isBlacklisted: FfiConverterOptionBool.read(from: &buf), + watchUntil: FfiConverterOptionString.read(from: &buf), + watchForBlockConfirmations: FfiConverterOptionUInt32.read(from: &buf), + updatedAt: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: IBtEstimateFeeResponse, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.feeSat, into: &buf) - FfiConverterTypeIBt0ConfMinTxFeeWindow.write(value.min0ConfTxFee, into: &buf) + public static func write(_ value: IGiftBtcAddress, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterSequenceString.write(value.transactions, into: &buf) + FfiConverterSequenceString.write(value.allTransactions, into: &buf) + FfiConverterOptionBool.write(value.isBlacklisted, into: &buf) + FfiConverterOptionString.write(value.watchUntil, into: &buf) + FfiConverterOptionUInt32.write(value.watchForBlockConfirmations, into: &buf) + FfiConverterOptionString.write(value.updatedAt, into: &buf) + FfiConverterOptionString.write(value.createdAt, into: &buf) } } @@ -5539,87 +7714,119 @@ public struct FfiConverterTypeIBtEstimateFeeResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtEstimateFeeResponse_lift(_ buf: RustBuffer) throws -> IBtEstimateFeeResponse { - return try FfiConverterTypeIBtEstimateFeeResponse.lift(buf) +public func FfiConverterTypeIGiftBtcAddress_lift(_ buf: RustBuffer) throws -> IGiftBtcAddress { + return try FfiConverterTypeIGiftBtcAddress.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtEstimateFeeResponse_lower(_ value: IBtEstimateFeeResponse) -> RustBuffer { - return FfiConverterTypeIBtEstimateFeeResponse.lower(value) +public func FfiConverterTypeIGiftBtcAddress_lower(_ value: IGiftBtcAddress) -> RustBuffer { + return FfiConverterTypeIGiftBtcAddress.lower(value) } -public struct IBtEstimateFeeResponse2 { - public var feeSat: UInt64 - public var networkFeeSat: UInt64 - public var serviceFeeSat: UInt64 - public var min0ConfTxFee: IBt0ConfMinTxFeeWindow +public struct IGiftCode { + public var id: String + public var code: String + public var createdAt: String + public var updatedAt: String + public var expiresAt: String + public var giftSat: UInt64? + public var scope: String? + public var maxCount: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, min0ConfTxFee: IBt0ConfMinTxFeeWindow) { - self.feeSat = feeSat - self.networkFeeSat = networkFeeSat - self.serviceFeeSat = serviceFeeSat - self.min0ConfTxFee = min0ConfTxFee + public init(id: String, code: String, createdAt: String, updatedAt: String, expiresAt: String, giftSat: UInt64?, scope: String?, maxCount: UInt32?) { + self.id = id + self.code = code + self.createdAt = createdAt + self.updatedAt = updatedAt + self.expiresAt = expiresAt + self.giftSat = giftSat + self.scope = scope + self.maxCount = maxCount } } #if compiler(>=6) -extension IBtEstimateFeeResponse2: Sendable {} +extension IGiftCode: Sendable {} #endif -extension IBtEstimateFeeResponse2: Equatable, Hashable { - public static func ==(lhs: IBtEstimateFeeResponse2, rhs: IBtEstimateFeeResponse2) -> Bool { - if lhs.feeSat != rhs.feeSat { +extension IGiftCode: Equatable, Hashable { + public static func ==(lhs: IGiftCode, rhs: IGiftCode) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.networkFeeSat != rhs.networkFeeSat { + if lhs.code != rhs.code { return false } - if lhs.serviceFeeSat != rhs.serviceFeeSat { + if lhs.createdAt != rhs.createdAt { return false } - if lhs.min0ConfTxFee != rhs.min0ConfTxFee { + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.expiresAt != rhs.expiresAt { + return false + } + if lhs.giftSat != rhs.giftSat { + return false + } + if lhs.scope != rhs.scope { + return false + } + if lhs.maxCount != rhs.maxCount { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(feeSat) - hasher.combine(networkFeeSat) - hasher.combine(serviceFeeSat) - hasher.combine(min0ConfTxFee) + hasher.combine(id) + hasher.combine(code) + hasher.combine(createdAt) + hasher.combine(updatedAt) + hasher.combine(expiresAt) + hasher.combine(giftSat) + hasher.combine(scope) + hasher.combine(maxCount) } } -extension IBtEstimateFeeResponse2: Codable {} +extension IGiftCode: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtEstimateFeeResponse2: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtEstimateFeeResponse2 { +public struct FfiConverterTypeIGiftCode: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftCode { return - try IBtEstimateFeeResponse2( - feeSat: FfiConverterUInt64.read(from: &buf), - networkFeeSat: FfiConverterUInt64.read(from: &buf), - serviceFeeSat: FfiConverterUInt64.read(from: &buf), - min0ConfTxFee: FfiConverterTypeIBt0ConfMinTxFeeWindow.read(from: &buf) + try IGiftCode( + id: FfiConverterString.read(from: &buf), + code: FfiConverterString.read(from: &buf), + createdAt: FfiConverterString.read(from: &buf), + updatedAt: FfiConverterString.read(from: &buf), + expiresAt: FfiConverterString.read(from: &buf), + giftSat: FfiConverterOptionUInt64.read(from: &buf), + scope: FfiConverterOptionString.read(from: &buf), + maxCount: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: IBtEstimateFeeResponse2, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.feeSat, into: &buf) - FfiConverterUInt64.write(value.networkFeeSat, into: &buf) - FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) - FfiConverterTypeIBt0ConfMinTxFeeWindow.write(value.min0ConfTxFee, into: &buf) + public static func write(_ value: IGiftCode, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.code, into: &buf) + FfiConverterString.write(value.createdAt, into: &buf) + FfiConverterString.write(value.updatedAt, into: &buf) + FfiConverterString.write(value.expiresAt, into: &buf) + FfiConverterOptionUInt64.write(value.giftSat, into: &buf) + FfiConverterOptionString.write(value.scope, into: &buf) + FfiConverterOptionUInt32.write(value.maxCount, into: &buf) } } @@ -5627,95 +7834,79 @@ public struct FfiConverterTypeIBtEstimateFeeResponse2: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtEstimateFeeResponse2_lift(_ buf: RustBuffer) throws -> IBtEstimateFeeResponse2 { - return try FfiConverterTypeIBtEstimateFeeResponse2.lift(buf) +public func FfiConverterTypeIGiftCode_lift(_ buf: RustBuffer) throws -> IGiftCode { + return try FfiConverterTypeIGiftCode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtEstimateFeeResponse2_lower(_ value: IBtEstimateFeeResponse2) -> RustBuffer { - return FfiConverterTypeIBtEstimateFeeResponse2.lower(value) +public func FfiConverterTypeIGiftCode_lower(_ value: IGiftCode) -> RustBuffer { + return FfiConverterTypeIGiftCode.lower(value) } -public struct IBtInfo { - public var version: UInt32 - public var nodes: [ILspNode] - public var options: IBtInfoOptions - public var versions: IBtInfoVersions - public var onchain: IBtInfoOnchain +public struct IGiftLspNode { + public var alias: String + public var pubkey: String + public var connectionStrings: [String] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(version: UInt32, nodes: [ILspNode], options: IBtInfoOptions, versions: IBtInfoVersions, onchain: IBtInfoOnchain) { - self.version = version - self.nodes = nodes - self.options = options - self.versions = versions - self.onchain = onchain + public init(alias: String, pubkey: String, connectionStrings: [String]) { + self.alias = alias + self.pubkey = pubkey + self.connectionStrings = connectionStrings } } #if compiler(>=6) -extension IBtInfo: Sendable {} +extension IGiftLspNode: Sendable {} #endif -extension IBtInfo: Equatable, Hashable { - public static func ==(lhs: IBtInfo, rhs: IBtInfo) -> Bool { - if lhs.version != rhs.version { - return false - } - if lhs.nodes != rhs.nodes { - return false - } - if lhs.options != rhs.options { +extension IGiftLspNode: Equatable, Hashable { + public static func ==(lhs: IGiftLspNode, rhs: IGiftLspNode) -> Bool { + if lhs.alias != rhs.alias { return false } - if lhs.versions != rhs.versions { + if lhs.pubkey != rhs.pubkey { return false } - if lhs.onchain != rhs.onchain { + if lhs.connectionStrings != rhs.connectionStrings { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(version) - hasher.combine(nodes) - hasher.combine(options) - hasher.combine(versions) - hasher.combine(onchain) + hasher.combine(alias) + hasher.combine(pubkey) + hasher.combine(connectionStrings) } } -extension IBtInfo: Codable {} +extension IGiftLspNode: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfo { +public struct FfiConverterTypeIGiftLspNode: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftLspNode { return - try IBtInfo( - version: FfiConverterUInt32.read(from: &buf), - nodes: FfiConverterSequenceTypeILspNode.read(from: &buf), - options: FfiConverterTypeIBtInfoOptions.read(from: &buf), - versions: FfiConverterTypeIBtInfoVersions.read(from: &buf), - onchain: FfiConverterTypeIBtInfoOnchain.read(from: &buf) + try IGiftLspNode( + alias: FfiConverterString.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + connectionStrings: FfiConverterSequenceString.read(from: &buf) ) } - public static func write(_ value: IBtInfo, into buf: inout [UInt8]) { - FfiConverterUInt32.write(value.version, into: &buf) - FfiConverterSequenceTypeILspNode.write(value.nodes, into: &buf) - FfiConverterTypeIBtInfoOptions.write(value.options, into: &buf) - FfiConverterTypeIBtInfoVersions.write(value.versions, into: &buf) - FfiConverterTypeIBtInfoOnchain.write(value.onchain, into: &buf) + public static func write(_ value: IGiftLspNode, into buf: inout [UInt8]) { + FfiConverterString.write(value.alias, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterSequenceString.write(value.connectionStrings, into: &buf) } } @@ -5723,71 +7914,231 @@ public struct FfiConverterTypeIBtInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfo_lift(_ buf: RustBuffer) throws -> IBtInfo { - return try FfiConverterTypeIBtInfo.lift(buf) +public func FfiConverterTypeIGiftLspNode_lift(_ buf: RustBuffer) throws -> IGiftLspNode { + return try FfiConverterTypeIGiftLspNode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfo_lower(_ value: IBtInfo) -> RustBuffer { - return FfiConverterTypeIBtInfo.lower(value) +public func FfiConverterTypeIGiftLspNode_lower(_ value: IGiftLspNode) -> RustBuffer { + return FfiConverterTypeIGiftLspNode.lower(value) } -public struct IBtInfoOnchain { - public var network: BitcoinNetworkEnum - public var feeRates: FeeRates +public struct IGiftOrder { + public var id: String + public var state: String + public var oldState: String? + public var isChannelExpired: Bool? + public var isOrderExpired: Bool? + public var lspBalanceSat: UInt64? + public var clientBalanceSat: UInt64? + public var channelExpiryWeeks: UInt32? + public var zeroConf: Bool? + public var zeroReserve: Bool? + public var announced: Bool? + public var clientNodeId: String? + public var channelExpiresAt: String? + public var orderExpiresAt: String? + public var feeSat: UInt64? + public var networkFeeSat: UInt64? + public var serviceFeeSat: UInt64? + public var payment: IGiftPayment? + public var lspNode: IGiftLspNode? + public var updatedAt: String? + public var createdAt: String? + public var nodeIdVerified: Bool? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(network: BitcoinNetworkEnum, feeRates: FeeRates) { - self.network = network - self.feeRates = feeRates + public init(id: String, state: String, oldState: String?, isChannelExpired: Bool?, isOrderExpired: Bool?, lspBalanceSat: UInt64?, clientBalanceSat: UInt64?, channelExpiryWeeks: UInt32?, zeroConf: Bool?, zeroReserve: Bool?, announced: Bool?, clientNodeId: String?, channelExpiresAt: String?, orderExpiresAt: String?, feeSat: UInt64?, networkFeeSat: UInt64?, serviceFeeSat: UInt64?, payment: IGiftPayment?, lspNode: IGiftLspNode?, updatedAt: String?, createdAt: String?, nodeIdVerified: Bool?) { + self.id = id + self.state = state + self.oldState = oldState + self.isChannelExpired = isChannelExpired + self.isOrderExpired = isOrderExpired + self.lspBalanceSat = lspBalanceSat + self.clientBalanceSat = clientBalanceSat + self.channelExpiryWeeks = channelExpiryWeeks + self.zeroConf = zeroConf + self.zeroReserve = zeroReserve + self.announced = announced + self.clientNodeId = clientNodeId + self.channelExpiresAt = channelExpiresAt + self.orderExpiresAt = orderExpiresAt + self.feeSat = feeSat + self.networkFeeSat = networkFeeSat + self.serviceFeeSat = serviceFeeSat + self.payment = payment + self.lspNode = lspNode + self.updatedAt = updatedAt + self.createdAt = createdAt + self.nodeIdVerified = nodeIdVerified } } #if compiler(>=6) -extension IBtInfoOnchain: Sendable {} +extension IGiftOrder: Sendable {} #endif -extension IBtInfoOnchain: Equatable, Hashable { - public static func ==(lhs: IBtInfoOnchain, rhs: IBtInfoOnchain) -> Bool { - if lhs.network != rhs.network { +extension IGiftOrder: Equatable, Hashable { + public static func ==(lhs: IGiftOrder, rhs: IGiftOrder) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.feeRates != rhs.feeRates { + if lhs.state != rhs.state { + return false + } + if lhs.oldState != rhs.oldState { + return false + } + if lhs.isChannelExpired != rhs.isChannelExpired { + return false + } + if lhs.isOrderExpired != rhs.isOrderExpired { + return false + } + if lhs.lspBalanceSat != rhs.lspBalanceSat { + return false + } + if lhs.clientBalanceSat != rhs.clientBalanceSat { + return false + } + if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { + return false + } + if lhs.zeroConf != rhs.zeroConf { + return false + } + if lhs.zeroReserve != rhs.zeroReserve { + return false + } + if lhs.announced != rhs.announced { + return false + } + if lhs.clientNodeId != rhs.clientNodeId { + return false + } + if lhs.channelExpiresAt != rhs.channelExpiresAt { + return false + } + if lhs.orderExpiresAt != rhs.orderExpiresAt { + return false + } + if lhs.feeSat != rhs.feeSat { + return false + } + if lhs.networkFeeSat != rhs.networkFeeSat { + return false + } + if lhs.serviceFeeSat != rhs.serviceFeeSat { + return false + } + if lhs.payment != rhs.payment { + return false + } + if lhs.lspNode != rhs.lspNode { + return false + } + if lhs.updatedAt != rhs.updatedAt { + return false + } + if lhs.createdAt != rhs.createdAt { + return false + } + if lhs.nodeIdVerified != rhs.nodeIdVerified { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(network) - hasher.combine(feeRates) + hasher.combine(id) + hasher.combine(state) + hasher.combine(oldState) + hasher.combine(isChannelExpired) + hasher.combine(isOrderExpired) + hasher.combine(lspBalanceSat) + hasher.combine(clientBalanceSat) + hasher.combine(channelExpiryWeeks) + hasher.combine(zeroConf) + hasher.combine(zeroReserve) + hasher.combine(announced) + hasher.combine(clientNodeId) + hasher.combine(channelExpiresAt) + hasher.combine(orderExpiresAt) + hasher.combine(feeSat) + hasher.combine(networkFeeSat) + hasher.combine(serviceFeeSat) + hasher.combine(payment) + hasher.combine(lspNode) + hasher.combine(updatedAt) + hasher.combine(createdAt) + hasher.combine(nodeIdVerified) } } -extension IBtInfoOnchain: Codable {} +extension IGiftOrder: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtInfoOnchain: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoOnchain { +public struct FfiConverterTypeIGiftOrder: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftOrder { return - try IBtInfoOnchain( - network: FfiConverterTypeBitcoinNetworkEnum.read(from: &buf), - feeRates: FfiConverterTypeFeeRates.read(from: &buf) + try IGiftOrder( + id: FfiConverterString.read(from: &buf), + state: FfiConverterString.read(from: &buf), + oldState: FfiConverterOptionString.read(from: &buf), + isChannelExpired: FfiConverterOptionBool.read(from: &buf), + isOrderExpired: FfiConverterOptionBool.read(from: &buf), + lspBalanceSat: FfiConverterOptionUInt64.read(from: &buf), + clientBalanceSat: FfiConverterOptionUInt64.read(from: &buf), + channelExpiryWeeks: FfiConverterOptionUInt32.read(from: &buf), + zeroConf: FfiConverterOptionBool.read(from: &buf), + zeroReserve: FfiConverterOptionBool.read(from: &buf), + announced: FfiConverterOptionBool.read(from: &buf), + clientNodeId: FfiConverterOptionString.read(from: &buf), + channelExpiresAt: FfiConverterOptionString.read(from: &buf), + orderExpiresAt: FfiConverterOptionString.read(from: &buf), + feeSat: FfiConverterOptionUInt64.read(from: &buf), + networkFeeSat: FfiConverterOptionUInt64.read(from: &buf), + serviceFeeSat: FfiConverterOptionUInt64.read(from: &buf), + payment: FfiConverterOptionTypeIGiftPayment.read(from: &buf), + lspNode: FfiConverterOptionTypeIGiftLspNode.read(from: &buf), + updatedAt: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionString.read(from: &buf), + nodeIdVerified: FfiConverterOptionBool.read(from: &buf) ) } - public static func write(_ value: IBtInfoOnchain, into buf: inout [UInt8]) { - FfiConverterTypeBitcoinNetworkEnum.write(value.network, into: &buf) - FfiConverterTypeFeeRates.write(value.feeRates, into: &buf) + public static func write(_ value: IGiftOrder, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.state, into: &buf) + FfiConverterOptionString.write(value.oldState, into: &buf) + FfiConverterOptionBool.write(value.isChannelExpired, into: &buf) + FfiConverterOptionBool.write(value.isOrderExpired, into: &buf) + FfiConverterOptionUInt64.write(value.lspBalanceSat, into: &buf) + FfiConverterOptionUInt64.write(value.clientBalanceSat, into: &buf) + FfiConverterOptionUInt32.write(value.channelExpiryWeeks, into: &buf) + FfiConverterOptionBool.write(value.zeroConf, into: &buf) + FfiConverterOptionBool.write(value.zeroReserve, into: &buf) + FfiConverterOptionBool.write(value.announced, into: &buf) + FfiConverterOptionString.write(value.clientNodeId, into: &buf) + FfiConverterOptionString.write(value.channelExpiresAt, into: &buf) + FfiConverterOptionString.write(value.orderExpiresAt, into: &buf) + FfiConverterOptionUInt64.write(value.feeSat, into: &buf) + FfiConverterOptionUInt64.write(value.networkFeeSat, into: &buf) + FfiConverterOptionUInt64.write(value.serviceFeeSat, into: &buf) + FfiConverterOptionTypeIGiftPayment.write(value.payment, into: &buf) + FfiConverterOptionTypeIGiftLspNode.write(value.lspNode, into: &buf) + FfiConverterOptionString.write(value.updatedAt, into: &buf) + FfiConverterOptionString.write(value.createdAt, into: &buf) + FfiConverterOptionBool.write(value.nodeIdVerified, into: &buf) } } @@ -5795,119 +8146,215 @@ public struct FfiConverterTypeIBtInfoOnchain: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoOnchain_lift(_ buf: RustBuffer) throws -> IBtInfoOnchain { - return try FfiConverterTypeIBtInfoOnchain.lift(buf) +public func FfiConverterTypeIGiftOrder_lift(_ buf: RustBuffer) throws -> IGiftOrder { + return try FfiConverterTypeIGiftOrder.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoOnchain_lower(_ value: IBtInfoOnchain) -> RustBuffer { - return FfiConverterTypeIBtInfoOnchain.lower(value) +public func FfiConverterTypeIGiftOrder_lower(_ value: IGiftOrder) -> RustBuffer { + return FfiConverterTypeIGiftOrder.lower(value) } -public struct IBtInfoOptions { - public var minChannelSizeSat: UInt64 - public var maxChannelSizeSat: UInt64 - public var minExpiryWeeks: UInt32 - public var maxExpiryWeeks: UInt32 - public var minPaymentConfirmations: UInt32 - public var minHighRiskPaymentConfirmations: UInt32 - public var max0ConfClientBalanceSat: UInt64 - public var maxClientBalanceSat: UInt64 +public struct IGiftPayment { + public var id: String + public var state: String + public var oldState: String? + public var onchainState: String? + public var lnState: String? + public var paidOnchainSat: UInt64? + public var paidLnSat: UInt64? + public var paidSat: UInt64? + public var isOverpaid: Bool? + public var isRefunded: Bool? + public var overpaidAmountSat: UInt64? + public var requiredOnchainConfirmations: UInt32? + public var settlementState: String? + public var expectedAmountSat: UInt64? + public var isManuallyPaid: Bool? + public var btcAddress: IGiftBtcAddress? + public var btcAddressId: String? + public var bolt11Invoice: IGiftBolt11Invoice? + public var bolt11InvoiceId: String? + public var manualRefunds: [String] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(minChannelSizeSat: UInt64, maxChannelSizeSat: UInt64, minExpiryWeeks: UInt32, maxExpiryWeeks: UInt32, minPaymentConfirmations: UInt32, minHighRiskPaymentConfirmations: UInt32, max0ConfClientBalanceSat: UInt64, maxClientBalanceSat: UInt64) { - self.minChannelSizeSat = minChannelSizeSat - self.maxChannelSizeSat = maxChannelSizeSat - self.minExpiryWeeks = minExpiryWeeks - self.maxExpiryWeeks = maxExpiryWeeks - self.minPaymentConfirmations = minPaymentConfirmations - self.minHighRiskPaymentConfirmations = minHighRiskPaymentConfirmations - self.max0ConfClientBalanceSat = max0ConfClientBalanceSat - self.maxClientBalanceSat = maxClientBalanceSat + public init(id: String, state: String, oldState: String?, onchainState: String?, lnState: String?, paidOnchainSat: UInt64?, paidLnSat: UInt64?, paidSat: UInt64?, isOverpaid: Bool?, isRefunded: Bool?, overpaidAmountSat: UInt64?, requiredOnchainConfirmations: UInt32?, settlementState: String?, expectedAmountSat: UInt64?, isManuallyPaid: Bool?, btcAddress: IGiftBtcAddress?, btcAddressId: String?, bolt11Invoice: IGiftBolt11Invoice?, bolt11InvoiceId: String?, manualRefunds: [String]) { + self.id = id + self.state = state + self.oldState = oldState + self.onchainState = onchainState + self.lnState = lnState + self.paidOnchainSat = paidOnchainSat + self.paidLnSat = paidLnSat + self.paidSat = paidSat + self.isOverpaid = isOverpaid + self.isRefunded = isRefunded + self.overpaidAmountSat = overpaidAmountSat + self.requiredOnchainConfirmations = requiredOnchainConfirmations + self.settlementState = settlementState + self.expectedAmountSat = expectedAmountSat + self.isManuallyPaid = isManuallyPaid + self.btcAddress = btcAddress + self.btcAddressId = btcAddressId + self.bolt11Invoice = bolt11Invoice + self.bolt11InvoiceId = bolt11InvoiceId + self.manualRefunds = manualRefunds } } #if compiler(>=6) -extension IBtInfoOptions: Sendable {} +extension IGiftPayment: Sendable {} #endif -extension IBtInfoOptions: Equatable, Hashable { - public static func ==(lhs: IBtInfoOptions, rhs: IBtInfoOptions) -> Bool { - if lhs.minChannelSizeSat != rhs.minChannelSizeSat { +extension IGiftPayment: Equatable, Hashable { + public static func ==(lhs: IGiftPayment, rhs: IGiftPayment) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.maxChannelSizeSat != rhs.maxChannelSizeSat { + if lhs.state != rhs.state { return false } - if lhs.minExpiryWeeks != rhs.minExpiryWeeks { + if lhs.oldState != rhs.oldState { return false } - if lhs.maxExpiryWeeks != rhs.maxExpiryWeeks { + if lhs.onchainState != rhs.onchainState { return false } - if lhs.minPaymentConfirmations != rhs.minPaymentConfirmations { + if lhs.lnState != rhs.lnState { return false } - if lhs.minHighRiskPaymentConfirmations != rhs.minHighRiskPaymentConfirmations { + if lhs.paidOnchainSat != rhs.paidOnchainSat { return false } - if lhs.max0ConfClientBalanceSat != rhs.max0ConfClientBalanceSat { + if lhs.paidLnSat != rhs.paidLnSat { return false } - if lhs.maxClientBalanceSat != rhs.maxClientBalanceSat { + if lhs.paidSat != rhs.paidSat { + return false + } + if lhs.isOverpaid != rhs.isOverpaid { + return false + } + if lhs.isRefunded != rhs.isRefunded { + return false + } + if lhs.overpaidAmountSat != rhs.overpaidAmountSat { + return false + } + if lhs.requiredOnchainConfirmations != rhs.requiredOnchainConfirmations { + return false + } + if lhs.settlementState != rhs.settlementState { + return false + } + if lhs.expectedAmountSat != rhs.expectedAmountSat { + return false + } + if lhs.isManuallyPaid != rhs.isManuallyPaid { + return false + } + if lhs.btcAddress != rhs.btcAddress { + return false + } + if lhs.btcAddressId != rhs.btcAddressId { + return false + } + if lhs.bolt11Invoice != rhs.bolt11Invoice { + return false + } + if lhs.bolt11InvoiceId != rhs.bolt11InvoiceId { + return false + } + if lhs.manualRefunds != rhs.manualRefunds { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(minChannelSizeSat) - hasher.combine(maxChannelSizeSat) - hasher.combine(minExpiryWeeks) - hasher.combine(maxExpiryWeeks) - hasher.combine(minPaymentConfirmations) - hasher.combine(minHighRiskPaymentConfirmations) - hasher.combine(max0ConfClientBalanceSat) - hasher.combine(maxClientBalanceSat) + hasher.combine(id) + hasher.combine(state) + hasher.combine(oldState) + hasher.combine(onchainState) + hasher.combine(lnState) + hasher.combine(paidOnchainSat) + hasher.combine(paidLnSat) + hasher.combine(paidSat) + hasher.combine(isOverpaid) + hasher.combine(isRefunded) + hasher.combine(overpaidAmountSat) + hasher.combine(requiredOnchainConfirmations) + hasher.combine(settlementState) + hasher.combine(expectedAmountSat) + hasher.combine(isManuallyPaid) + hasher.combine(btcAddress) + hasher.combine(btcAddressId) + hasher.combine(bolt11Invoice) + hasher.combine(bolt11InvoiceId) + hasher.combine(manualRefunds) } } -extension IBtInfoOptions: Codable {} +extension IGiftPayment: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtInfoOptions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoOptions { +public struct FfiConverterTypeIGiftPayment: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftPayment { return - try IBtInfoOptions( - minChannelSizeSat: FfiConverterUInt64.read(from: &buf), - maxChannelSizeSat: FfiConverterUInt64.read(from: &buf), - minExpiryWeeks: FfiConverterUInt32.read(from: &buf), - maxExpiryWeeks: FfiConverterUInt32.read(from: &buf), - minPaymentConfirmations: FfiConverterUInt32.read(from: &buf), - minHighRiskPaymentConfirmations: FfiConverterUInt32.read(from: &buf), - max0ConfClientBalanceSat: FfiConverterUInt64.read(from: &buf), - maxClientBalanceSat: FfiConverterUInt64.read(from: &buf) + try IGiftPayment( + id: FfiConverterString.read(from: &buf), + state: FfiConverterString.read(from: &buf), + oldState: FfiConverterOptionString.read(from: &buf), + onchainState: FfiConverterOptionString.read(from: &buf), + lnState: FfiConverterOptionString.read(from: &buf), + paidOnchainSat: FfiConverterOptionUInt64.read(from: &buf), + paidLnSat: FfiConverterOptionUInt64.read(from: &buf), + paidSat: FfiConverterOptionUInt64.read(from: &buf), + isOverpaid: FfiConverterOptionBool.read(from: &buf), + isRefunded: FfiConverterOptionBool.read(from: &buf), + overpaidAmountSat: FfiConverterOptionUInt64.read(from: &buf), + requiredOnchainConfirmations: FfiConverterOptionUInt32.read(from: &buf), + settlementState: FfiConverterOptionString.read(from: &buf), + expectedAmountSat: FfiConverterOptionUInt64.read(from: &buf), + isManuallyPaid: FfiConverterOptionBool.read(from: &buf), + btcAddress: FfiConverterOptionTypeIGiftBtcAddress.read(from: &buf), + btcAddressId: FfiConverterOptionString.read(from: &buf), + bolt11Invoice: FfiConverterOptionTypeIGiftBolt11Invoice.read(from: &buf), + bolt11InvoiceId: FfiConverterOptionString.read(from: &buf), + manualRefunds: FfiConverterSequenceString.read(from: &buf) ) } - public static func write(_ value: IBtInfoOptions, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.minChannelSizeSat, into: &buf) - FfiConverterUInt64.write(value.maxChannelSizeSat, into: &buf) - FfiConverterUInt32.write(value.minExpiryWeeks, into: &buf) - FfiConverterUInt32.write(value.maxExpiryWeeks, into: &buf) - FfiConverterUInt32.write(value.minPaymentConfirmations, into: &buf) - FfiConverterUInt32.write(value.minHighRiskPaymentConfirmations, into: &buf) - FfiConverterUInt64.write(value.max0ConfClientBalanceSat, into: &buf) - FfiConverterUInt64.write(value.maxClientBalanceSat, into: &buf) + public static func write(_ value: IGiftPayment, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.state, into: &buf) + FfiConverterOptionString.write(value.oldState, into: &buf) + FfiConverterOptionString.write(value.onchainState, into: &buf) + FfiConverterOptionString.write(value.lnState, into: &buf) + FfiConverterOptionUInt64.write(value.paidOnchainSat, into: &buf) + FfiConverterOptionUInt64.write(value.paidLnSat, into: &buf) + FfiConverterOptionUInt64.write(value.paidSat, into: &buf) + FfiConverterOptionBool.write(value.isOverpaid, into: &buf) + FfiConverterOptionBool.write(value.isRefunded, into: &buf) + FfiConverterOptionUInt64.write(value.overpaidAmountSat, into: &buf) + FfiConverterOptionUInt32.write(value.requiredOnchainConfirmations, into: &buf) + FfiConverterOptionString.write(value.settlementState, into: &buf) + FfiConverterOptionUInt64.write(value.expectedAmountSat, into: &buf) + FfiConverterOptionBool.write(value.isManuallyPaid, into: &buf) + FfiConverterOptionTypeIGiftBtcAddress.write(value.btcAddress, into: &buf) + FfiConverterOptionString.write(value.btcAddressId, into: &buf) + FfiConverterOptionTypeIGiftBolt11Invoice.write(value.bolt11Invoice, into: &buf) + FfiConverterOptionString.write(value.bolt11InvoiceId, into: &buf) + FfiConverterSequenceString.write(value.manualRefunds, into: &buf) } } @@ -5915,79 +8362,87 @@ public struct FfiConverterTypeIBtInfoOptions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoOptions_lift(_ buf: RustBuffer) throws -> IBtInfoOptions { - return try FfiConverterTypeIBtInfoOptions.lift(buf) +public func FfiConverterTypeIGiftPayment_lift(_ buf: RustBuffer) throws -> IGiftPayment { + return try FfiConverterTypeIGiftPayment.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoOptions_lower(_ value: IBtInfoOptions) -> RustBuffer { - return FfiConverterTypeIBtInfoOptions.lower(value) +public func FfiConverterTypeIGiftPayment_lower(_ value: IGiftPayment) -> RustBuffer { + return FfiConverterTypeIGiftPayment.lower(value) } -public struct IBtInfoVersions { - public var http: String - public var btc: String - public var ln2: String +public struct ILspNode { + public var alias: String + public var pubkey: String + public var connectionStrings: [String] + public var readonly: Bool? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(http: String, btc: String, ln2: String) { - self.http = http - self.btc = btc - self.ln2 = ln2 + public init(alias: String, pubkey: String, connectionStrings: [String], readonly: Bool?) { + self.alias = alias + self.pubkey = pubkey + self.connectionStrings = connectionStrings + self.readonly = readonly } } #if compiler(>=6) -extension IBtInfoVersions: Sendable {} +extension ILspNode: Sendable {} #endif -extension IBtInfoVersions: Equatable, Hashable { - public static func ==(lhs: IBtInfoVersions, rhs: IBtInfoVersions) -> Bool { - if lhs.http != rhs.http { +extension ILspNode: Equatable, Hashable { + public static func ==(lhs: ILspNode, rhs: ILspNode) -> Bool { + if lhs.alias != rhs.alias { return false } - if lhs.btc != rhs.btc { + if lhs.pubkey != rhs.pubkey { return false } - if lhs.ln2 != rhs.ln2 { + if lhs.connectionStrings != rhs.connectionStrings { + return false + } + if lhs.readonly != rhs.readonly { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(http) - hasher.combine(btc) - hasher.combine(ln2) + hasher.combine(alias) + hasher.combine(pubkey) + hasher.combine(connectionStrings) + hasher.combine(readonly) } } -extension IBtInfoVersions: Codable {} +extension ILspNode: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtInfoVersions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtInfoVersions { - return - try IBtInfoVersions( - http: FfiConverterString.read(from: &buf), - btc: FfiConverterString.read(from: &buf), - ln2: FfiConverterString.read(from: &buf) +public struct FfiConverterTypeILspNode: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ILspNode { + return + try ILspNode( + alias: FfiConverterString.read(from: &buf), + pubkey: FfiConverterString.read(from: &buf), + connectionStrings: FfiConverterSequenceString.read(from: &buf), + readonly: FfiConverterOptionBool.read(from: &buf) ) } - public static func write(_ value: IBtInfoVersions, into buf: inout [UInt8]) { - FfiConverterString.write(value.http, into: &buf) - FfiConverterString.write(value.btc, into: &buf) - FfiConverterString.write(value.ln2, into: &buf) + public static func write(_ value: ILspNode, into buf: inout [UInt8]) { + FfiConverterString.write(value.alias, into: &buf) + FfiConverterString.write(value.pubkey, into: &buf) + FfiConverterSequenceString.write(value.connectionStrings, into: &buf) + FfiConverterOptionBool.write(value.readonly, into: &buf) } } @@ -5995,71 +8450,66 @@ public struct FfiConverterTypeIBtInfoVersions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoVersions_lift(_ buf: RustBuffer) throws -> IBtInfoVersions { - return try FfiConverterTypeIBtInfoVersions.lift(buf) +public func FfiConverterTypeILspNode_lift(_ buf: RustBuffer) throws -> ILspNode { + return try FfiConverterTypeILspNode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtInfoVersions_lower(_ value: IBtInfoVersions) -> RustBuffer { - return FfiConverterTypeIBtInfoVersions.lower(value) +public func FfiConverterTypeILspNode_lower(_ value: ILspNode) -> RustBuffer { + return FfiConverterTypeILspNode.lower(value) } -public struct IBtOnchainTransaction { +public struct IManualRefund { public var amountSat: UInt64 - public var txId: String - public var vout: UInt32 - public var blockHeight: UInt32? - public var blockConfirmationCount: UInt32 - public var feeRateSatPerVbyte: Double - public var confirmed: Bool - public var suspicious0ConfReason: String + public var target: String + public var state: ManualRefundStateEnum + public var createdByName: String + public var votedByName: String? + public var reason: String? + public var targetType: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(amountSat: UInt64, txId: String, vout: UInt32, blockHeight: UInt32?, blockConfirmationCount: UInt32, feeRateSatPerVbyte: Double, confirmed: Bool, suspicious0ConfReason: String) { + public init(amountSat: UInt64, target: String, state: ManualRefundStateEnum, createdByName: String, votedByName: String?, reason: String?, targetType: String) { self.amountSat = amountSat - self.txId = txId - self.vout = vout - self.blockHeight = blockHeight - self.blockConfirmationCount = blockConfirmationCount - self.feeRateSatPerVbyte = feeRateSatPerVbyte - self.confirmed = confirmed - self.suspicious0ConfReason = suspicious0ConfReason + self.target = target + self.state = state + self.createdByName = createdByName + self.votedByName = votedByName + self.reason = reason + self.targetType = targetType } } #if compiler(>=6) -extension IBtOnchainTransaction: Sendable {} +extension IManualRefund: Sendable {} #endif -extension IBtOnchainTransaction: Equatable, Hashable { - public static func ==(lhs: IBtOnchainTransaction, rhs: IBtOnchainTransaction) -> Bool { +extension IManualRefund: Equatable, Hashable { + public static func ==(lhs: IManualRefund, rhs: IManualRefund) -> Bool { if lhs.amountSat != rhs.amountSat { return false } - if lhs.txId != rhs.txId { - return false - } - if lhs.vout != rhs.vout { + if lhs.target != rhs.target { return false } - if lhs.blockHeight != rhs.blockHeight { + if lhs.state != rhs.state { return false } - if lhs.blockConfirmationCount != rhs.blockConfirmationCount { + if lhs.createdByName != rhs.createdByName { return false } - if lhs.feeRateSatPerVbyte != rhs.feeRateSatPerVbyte { + if lhs.votedByName != rhs.votedByName { return false } - if lhs.confirmed != rhs.confirmed { + if lhs.reason != rhs.reason { return false } - if lhs.suspicious0ConfReason != rhs.suspicious0ConfReason { + if lhs.targetType != rhs.targetType { return false } return true @@ -6067,47 +8517,44 @@ extension IBtOnchainTransaction: Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(amountSat) - hasher.combine(txId) - hasher.combine(vout) - hasher.combine(blockHeight) - hasher.combine(blockConfirmationCount) - hasher.combine(feeRateSatPerVbyte) - hasher.combine(confirmed) - hasher.combine(suspicious0ConfReason) + hasher.combine(target) + hasher.combine(state) + hasher.combine(createdByName) + hasher.combine(votedByName) + hasher.combine(reason) + hasher.combine(targetType) } } -extension IBtOnchainTransaction: Codable {} +extension IManualRefund: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtOnchainTransaction: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOnchainTransaction { +public struct FfiConverterTypeIManualRefund: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IManualRefund { return - try IBtOnchainTransaction( + try IManualRefund( amountSat: FfiConverterUInt64.read(from: &buf), - txId: FfiConverterString.read(from: &buf), - vout: FfiConverterUInt32.read(from: &buf), - blockHeight: FfiConverterOptionUInt32.read(from: &buf), - blockConfirmationCount: FfiConverterUInt32.read(from: &buf), - feeRateSatPerVbyte: FfiConverterDouble.read(from: &buf), - confirmed: FfiConverterBool.read(from: &buf), - suspicious0ConfReason: FfiConverterString.read(from: &buf) + target: FfiConverterString.read(from: &buf), + state: FfiConverterTypeManualRefundStateEnum.read(from: &buf), + createdByName: FfiConverterString.read(from: &buf), + votedByName: FfiConverterOptionString.read(from: &buf), + reason: FfiConverterOptionString.read(from: &buf), + targetType: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IBtOnchainTransaction, into buf: inout [UInt8]) { + public static func write(_ value: IManualRefund, into buf: inout [UInt8]) { FfiConverterUInt64.write(value.amountSat, into: &buf) - FfiConverterString.write(value.txId, into: &buf) - FfiConverterUInt32.write(value.vout, into: &buf) - FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) - FfiConverterUInt32.write(value.blockConfirmationCount, into: &buf) - FfiConverterDouble.write(value.feeRateSatPerVbyte, into: &buf) - FfiConverterBool.write(value.confirmed, into: &buf) - FfiConverterString.write(value.suspicious0ConfReason, into: &buf) + FfiConverterString.write(value.target, into: &buf) + FfiConverterTypeManualRefundStateEnum.write(value.state, into: &buf) + FfiConverterString.write(value.createdByName, into: &buf) + FfiConverterOptionString.write(value.votedByName, into: &buf) + FfiConverterOptionString.write(value.reason, into: &buf) + FfiConverterString.write(value.targetType, into: &buf) } } @@ -6115,87 +8562,79 @@ public struct FfiConverterTypeIBtOnchainTransaction: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOnchainTransaction_lift(_ buf: RustBuffer) throws -> IBtOnchainTransaction { - return try FfiConverterTypeIBtOnchainTransaction.lift(buf) +public func FfiConverterTypeIManualRefund_lift(_ buf: RustBuffer) throws -> IManualRefund { + return try FfiConverterTypeIManualRefund.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOnchainTransaction_lower(_ value: IBtOnchainTransaction) -> RustBuffer { - return FfiConverterTypeIBtOnchainTransaction.lower(value) +public func FfiConverterTypeIManualRefund_lower(_ value: IManualRefund) -> RustBuffer { + return FfiConverterTypeIManualRefund.lower(value) } -public struct IBtOnchainTransactions { - public var address: String - public var confirmedSat: UInt64 - public var requiredConfirmations: UInt32 - public var transactions: [IBtOnchainTransaction] +public struct JadeAccount { + public var variant: JadeAddressVariant + public var xpub: String + public var derivationPath: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(address: String, confirmedSat: UInt64, requiredConfirmations: UInt32, transactions: [IBtOnchainTransaction]) { - self.address = address - self.confirmedSat = confirmedSat - self.requiredConfirmations = requiredConfirmations - self.transactions = transactions + public init(variant: JadeAddressVariant, xpub: String, derivationPath: String) { + self.variant = variant + self.xpub = xpub + self.derivationPath = derivationPath } } #if compiler(>=6) -extension IBtOnchainTransactions: Sendable {} +extension JadeAccount: Sendable {} #endif -extension IBtOnchainTransactions: Equatable, Hashable { - public static func ==(lhs: IBtOnchainTransactions, rhs: IBtOnchainTransactions) -> Bool { - if lhs.address != rhs.address { - return false - } - if lhs.confirmedSat != rhs.confirmedSat { +extension JadeAccount: Equatable, Hashable { + public static func ==(lhs: JadeAccount, rhs: JadeAccount) -> Bool { + if lhs.variant != rhs.variant { return false } - if lhs.requiredConfirmations != rhs.requiredConfirmations { + if lhs.xpub != rhs.xpub { return false } - if lhs.transactions != rhs.transactions { + if lhs.derivationPath != rhs.derivationPath { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(confirmedSat) - hasher.combine(requiredConfirmations) - hasher.combine(transactions) + hasher.combine(variant) + hasher.combine(xpub) + hasher.combine(derivationPath) } } -extension IBtOnchainTransactions: Codable {} +extension JadeAccount: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtOnchainTransactions: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOnchainTransactions { +public struct FfiConverterTypeJadeAccount: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAccount { return - try IBtOnchainTransactions( - address: FfiConverterString.read(from: &buf), - confirmedSat: FfiConverterUInt64.read(from: &buf), - requiredConfirmations: FfiConverterUInt32.read(from: &buf), - transactions: FfiConverterSequenceTypeIBtOnchainTransaction.read(from: &buf) + try JadeAccount( + variant: FfiConverterTypeJadeAddressVariant.read(from: &buf), + xpub: FfiConverterString.read(from: &buf), + derivationPath: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IBtOnchainTransactions, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterUInt64.write(value.confirmedSat, into: &buf) - FfiConverterUInt32.write(value.requiredConfirmations, into: &buf) - FfiConverterSequenceTypeIBtOnchainTransaction.write(value.transactions, into: &buf) + public static func write(_ value: JadeAccount, into buf: inout [UInt8]) { + FfiConverterTypeJadeAddressVariant.write(value.variant, into: &buf) + FfiConverterString.write(value.xpub, into: &buf) + FfiConverterString.write(value.derivationPath, into: &buf) } } @@ -6203,239 +8642,79 @@ public struct FfiConverterTypeIBtOnchainTransactions: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOnchainTransactions_lift(_ buf: RustBuffer) throws -> IBtOnchainTransactions { - return try FfiConverterTypeIBtOnchainTransactions.lift(buf) +public func FfiConverterTypeJadeAccount_lift(_ buf: RustBuffer) throws -> JadeAccount { + return try FfiConverterTypeJadeAccount.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOnchainTransactions_lower(_ value: IBtOnchainTransactions) -> RustBuffer { - return FfiConverterTypeIBtOnchainTransactions.lower(value) +public func FfiConverterTypeJadeAccount_lower(_ value: JadeAccount) -> RustBuffer { + return FfiConverterTypeJadeAccount.lower(value) } - - -public struct IBtOrder { - public var id: String - public var state: BtOrderState - public var state2: BtOrderState2? - public var feeSat: UInt64 - public var networkFeeSat: UInt64 - public var serviceFeeSat: UInt64 - public var lspBalanceSat: UInt64 - public var clientBalanceSat: UInt64 - public var zeroConf: Bool - public var zeroReserve: Bool - public var clientNodeId: String? - public var channelExpiryWeeks: UInt32 - public var channelExpiresAt: String - public var orderExpiresAt: String - public var channel: IBtChannel? - public var lspNode: ILspNode? - public var lnurl: String? - public var payment: IBtPayment? - public var couponCode: String? - public var source: String? - public var discount: IDiscount? - public var updatedAt: String - public var createdAt: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(id: String, state: BtOrderState, state2: BtOrderState2?, feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, lspBalanceSat: UInt64, clientBalanceSat: UInt64, zeroConf: Bool, zeroReserve: Bool, clientNodeId: String?, channelExpiryWeeks: UInt32, channelExpiresAt: String, orderExpiresAt: String, channel: IBtChannel?, lspNode: ILspNode?, lnurl: String?, payment: IBtPayment?, couponCode: String?, source: String?, discount: IDiscount?, updatedAt: String, createdAt: String) { - self.id = id - self.state = state - self.state2 = state2 - self.feeSat = feeSat - self.networkFeeSat = networkFeeSat - self.serviceFeeSat = serviceFeeSat - self.lspBalanceSat = lspBalanceSat - self.clientBalanceSat = clientBalanceSat - self.zeroConf = zeroConf - self.zeroReserve = zeroReserve - self.clientNodeId = clientNodeId - self.channelExpiryWeeks = channelExpiryWeeks - self.channelExpiresAt = channelExpiresAt - self.orderExpiresAt = orderExpiresAt - self.channel = channel - self.lspNode = lspNode - self.lnurl = lnurl - self.payment = payment - self.couponCode = couponCode - self.source = source - self.discount = discount - self.updatedAt = updatedAt - self.createdAt = createdAt + + +public struct JadeAccountExport { + public var masterFingerprint: String + public var accountIndex: UInt32 + public var accounts: [JadeAccount] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(masterFingerprint: String, accountIndex: UInt32, accounts: [JadeAccount]) { + self.masterFingerprint = masterFingerprint + self.accountIndex = accountIndex + self.accounts = accounts } } #if compiler(>=6) -extension IBtOrder: Sendable {} +extension JadeAccountExport: Sendable {} #endif -extension IBtOrder: Equatable, Hashable { - public static func ==(lhs: IBtOrder, rhs: IBtOrder) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.state != rhs.state { - return false - } - if lhs.state2 != rhs.state2 { - return false - } - if lhs.feeSat != rhs.feeSat { - return false - } - if lhs.networkFeeSat != rhs.networkFeeSat { - return false - } - if lhs.serviceFeeSat != rhs.serviceFeeSat { - return false - } - if lhs.lspBalanceSat != rhs.lspBalanceSat { - return false - } - if lhs.clientBalanceSat != rhs.clientBalanceSat { - return false - } - if lhs.zeroConf != rhs.zeroConf { - return false - } - if lhs.zeroReserve != rhs.zeroReserve { - return false - } - if lhs.clientNodeId != rhs.clientNodeId { - return false - } - if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { - return false - } - if lhs.channelExpiresAt != rhs.channelExpiresAt { - return false - } - if lhs.orderExpiresAt != rhs.orderExpiresAt { - return false - } - if lhs.channel != rhs.channel { - return false - } - if lhs.lspNode != rhs.lspNode { - return false - } - if lhs.lnurl != rhs.lnurl { - return false - } - if lhs.payment != rhs.payment { - return false - } - if lhs.couponCode != rhs.couponCode { - return false - } - if lhs.source != rhs.source { - return false - } - if lhs.discount != rhs.discount { +extension JadeAccountExport: Equatable, Hashable { + public static func ==(lhs: JadeAccountExport, rhs: JadeAccountExport) -> Bool { + if lhs.masterFingerprint != rhs.masterFingerprint { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.accountIndex != rhs.accountIndex { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.accounts != rhs.accounts { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(state) - hasher.combine(state2) - hasher.combine(feeSat) - hasher.combine(networkFeeSat) - hasher.combine(serviceFeeSat) - hasher.combine(lspBalanceSat) - hasher.combine(clientBalanceSat) - hasher.combine(zeroConf) - hasher.combine(zeroReserve) - hasher.combine(clientNodeId) - hasher.combine(channelExpiryWeeks) - hasher.combine(channelExpiresAt) - hasher.combine(orderExpiresAt) - hasher.combine(channel) - hasher.combine(lspNode) - hasher.combine(lnurl) - hasher.combine(payment) - hasher.combine(couponCode) - hasher.combine(source) - hasher.combine(discount) - hasher.combine(updatedAt) - hasher.combine(createdAt) + hasher.combine(masterFingerprint) + hasher.combine(accountIndex) + hasher.combine(accounts) } } -extension IBtOrder: Codable {} +extension JadeAccountExport: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtOrder: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtOrder { +public struct FfiConverterTypeJadeAccountExport: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAccountExport { return - try IBtOrder( - id: FfiConverterString.read(from: &buf), - state: FfiConverterTypeBtOrderState.read(from: &buf), - state2: FfiConverterOptionTypeBtOrderState2.read(from: &buf), - feeSat: FfiConverterUInt64.read(from: &buf), - networkFeeSat: FfiConverterUInt64.read(from: &buf), - serviceFeeSat: FfiConverterUInt64.read(from: &buf), - lspBalanceSat: FfiConverterUInt64.read(from: &buf), - clientBalanceSat: FfiConverterUInt64.read(from: &buf), - zeroConf: FfiConverterBool.read(from: &buf), - zeroReserve: FfiConverterBool.read(from: &buf), - clientNodeId: FfiConverterOptionString.read(from: &buf), - channelExpiryWeeks: FfiConverterUInt32.read(from: &buf), - channelExpiresAt: FfiConverterString.read(from: &buf), - orderExpiresAt: FfiConverterString.read(from: &buf), - channel: FfiConverterOptionTypeIBtChannel.read(from: &buf), - lspNode: FfiConverterOptionTypeILspNode.read(from: &buf), - lnurl: FfiConverterOptionString.read(from: &buf), - payment: FfiConverterOptionTypeIBtPayment.read(from: &buf), - couponCode: FfiConverterOptionString.read(from: &buf), - source: FfiConverterOptionString.read(from: &buf), - discount: FfiConverterOptionTypeIDiscount.read(from: &buf), - updatedAt: FfiConverterString.read(from: &buf), - createdAt: FfiConverterString.read(from: &buf) + try JadeAccountExport( + masterFingerprint: FfiConverterString.read(from: &buf), + accountIndex: FfiConverterUInt32.read(from: &buf), + accounts: FfiConverterSequenceTypeJadeAccount.read(from: &buf) ) } - public static func write(_ value: IBtOrder, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypeBtOrderState.write(value.state, into: &buf) - FfiConverterOptionTypeBtOrderState2.write(value.state2, into: &buf) - FfiConverterUInt64.write(value.feeSat, into: &buf) - FfiConverterUInt64.write(value.networkFeeSat, into: &buf) - FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) - FfiConverterUInt64.write(value.lspBalanceSat, into: &buf) - FfiConverterUInt64.write(value.clientBalanceSat, into: &buf) - FfiConverterBool.write(value.zeroConf, into: &buf) - FfiConverterBool.write(value.zeroReserve, into: &buf) - FfiConverterOptionString.write(value.clientNodeId, into: &buf) - FfiConverterUInt32.write(value.channelExpiryWeeks, into: &buf) - FfiConverterString.write(value.channelExpiresAt, into: &buf) - FfiConverterString.write(value.orderExpiresAt, into: &buf) - FfiConverterOptionTypeIBtChannel.write(value.channel, into: &buf) - FfiConverterOptionTypeILspNode.write(value.lspNode, into: &buf) - FfiConverterOptionString.write(value.lnurl, into: &buf) - FfiConverterOptionTypeIBtPayment.write(value.payment, into: &buf) - FfiConverterOptionString.write(value.couponCode, into: &buf) - FfiConverterOptionString.write(value.source, into: &buf) - FfiConverterOptionTypeIDiscount.write(value.discount, into: &buf) - FfiConverterString.write(value.updatedAt, into: &buf) - FfiConverterString.write(value.createdAt, into: &buf) + public static func write(_ value: JadeAccountExport, into buf: inout [UInt8]) { + FfiConverterString.write(value.masterFingerprint, into: &buf) + FfiConverterUInt32.write(value.accountIndex, into: &buf) + FfiConverterSequenceTypeJadeAccount.write(value.accounts, into: &buf) } } @@ -6443,111 +8722,87 @@ public struct FfiConverterTypeIBtOrder: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOrder_lift(_ buf: RustBuffer) throws -> IBtOrder { - return try FfiConverterTypeIBtOrder.lift(buf) +public func FfiConverterTypeJadeAccountExport_lift(_ buf: RustBuffer) throws -> JadeAccountExport { + return try FfiConverterTypeJadeAccountExport.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtOrder_lower(_ value: IBtOrder) -> RustBuffer { - return FfiConverterTypeIBtOrder.lower(value) +public func FfiConverterTypeJadeAccountExport_lower(_ value: JadeAccountExport) -> RustBuffer { + return FfiConverterTypeJadeAccountExport.lower(value) } -public struct IBtPayment { - public var state: BtPaymentState - public var state2: BtPaymentState2? - public var paidSat: UInt64 - public var bolt11Invoice: IBtBolt11Invoice? - public var onchain: IBtOnchainTransactions? - public var isManuallyPaid: Bool? - public var manualRefunds: [IManualRefund]? +public struct JadeDeviceInfo { + public var path: String + public var transport: JadeTransportKind + public var name: String? + public var serialNumber: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(state: BtPaymentState, state2: BtPaymentState2?, paidSat: UInt64, bolt11Invoice: IBtBolt11Invoice?, onchain: IBtOnchainTransactions?, isManuallyPaid: Bool?, manualRefunds: [IManualRefund]?) { - self.state = state - self.state2 = state2 - self.paidSat = paidSat - self.bolt11Invoice = bolt11Invoice - self.onchain = onchain - self.isManuallyPaid = isManuallyPaid - self.manualRefunds = manualRefunds + public init(path: String, transport: JadeTransportKind, name: String?, serialNumber: String?) { + self.path = path + self.transport = transport + self.name = name + self.serialNumber = serialNumber } } #if compiler(>=6) -extension IBtPayment: Sendable {} +extension JadeDeviceInfo: Sendable {} #endif -extension IBtPayment: Equatable, Hashable { - public static func ==(lhs: IBtPayment, rhs: IBtPayment) -> Bool { - if lhs.state != rhs.state { - return false - } - if lhs.state2 != rhs.state2 { - return false - } - if lhs.paidSat != rhs.paidSat { - return false - } - if lhs.bolt11Invoice != rhs.bolt11Invoice { +extension JadeDeviceInfo: Equatable, Hashable { + public static func ==(lhs: JadeDeviceInfo, rhs: JadeDeviceInfo) -> Bool { + if lhs.path != rhs.path { return false } - if lhs.onchain != rhs.onchain { + if lhs.transport != rhs.transport { return false } - if lhs.isManuallyPaid != rhs.isManuallyPaid { + if lhs.name != rhs.name { return false } - if lhs.manualRefunds != rhs.manualRefunds { + if lhs.serialNumber != rhs.serialNumber { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(state) - hasher.combine(state2) - hasher.combine(paidSat) - hasher.combine(bolt11Invoice) - hasher.combine(onchain) - hasher.combine(isManuallyPaid) - hasher.combine(manualRefunds) + hasher.combine(path) + hasher.combine(transport) + hasher.combine(name) + hasher.combine(serialNumber) } } -extension IBtPayment: Codable {} +extension JadeDeviceInfo: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIBtPayment: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IBtPayment { +public struct FfiConverterTypeJadeDeviceInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeDeviceInfo { return - try IBtPayment( - state: FfiConverterTypeBtPaymentState.read(from: &buf), - state2: FfiConverterOptionTypeBtPaymentState2.read(from: &buf), - paidSat: FfiConverterUInt64.read(from: &buf), - bolt11Invoice: FfiConverterOptionTypeIBtBolt11Invoice.read(from: &buf), - onchain: FfiConverterOptionTypeIBtOnchainTransactions.read(from: &buf), - isManuallyPaid: FfiConverterOptionBool.read(from: &buf), - manualRefunds: FfiConverterOptionSequenceTypeIManualRefund.read(from: &buf) + try JadeDeviceInfo( + path: FfiConverterString.read(from: &buf), + transport: FfiConverterTypeJadeTransportKind.read(from: &buf), + name: FfiConverterOptionString.read(from: &buf), + serialNumber: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: IBtPayment, into buf: inout [UInt8]) { - FfiConverterTypeBtPaymentState.write(value.state, into: &buf) - FfiConverterOptionTypeBtPaymentState2.write(value.state2, into: &buf) - FfiConverterUInt64.write(value.paidSat, into: &buf) - FfiConverterOptionTypeIBtBolt11Invoice.write(value.bolt11Invoice, into: &buf) - FfiConverterOptionTypeIBtOnchainTransactions.write(value.onchain, into: &buf) - FfiConverterOptionBool.write(value.isManuallyPaid, into: &buf) - FfiConverterOptionSequenceTypeIManualRefund.write(value.manualRefunds, into: &buf) + public static func write(_ value: JadeDeviceInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterTypeJadeTransportKind.write(value.transport, into: &buf) + FfiConverterOptionString.write(value.name, into: &buf) + FfiConverterOptionString.write(value.serialNumber, into: &buf) } } @@ -6555,199 +8810,102 @@ public struct FfiConverterTypeIBtPayment: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtPayment_lift(_ buf: RustBuffer) throws -> IBtPayment { - return try FfiConverterTypeIBtPayment.lift(buf) +public func FfiConverterTypeJadeDeviceInfo_lift(_ buf: RustBuffer) throws -> JadeDeviceInfo { + return try FfiConverterTypeJadeDeviceInfo.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIBtPayment_lower(_ value: IBtPayment) -> RustBuffer { - return FfiConverterTypeIBtPayment.lower(value) +public func FfiConverterTypeJadeDeviceInfo_lower(_ value: JadeDeviceInfo) -> RustBuffer { + return FfiConverterTypeJadeDeviceInfo.lower(value) } -public struct IcJitEntry { - public var id: String - public var state: CJitStateEnum - public var feeSat: UInt64 - public var networkFeeSat: UInt64 - public var serviceFeeSat: UInt64 - public var channelSizeSat: UInt64 - public var channelExpiryWeeks: UInt32 - public var channelOpenError: String? - public var nodeId: String - public var invoice: IBtBolt11Invoice - public var channel: IBtChannel? - public var lspNode: ILspNode - public var couponCode: String - public var source: String? - public var discount: IDiscount? - public var expiresAt: String - public var updatedAt: String - public var createdAt: String +/** + * A device the native layer discovered. + */ +public struct JadeNativeDevice { + /** + * Transport specific address: a BLE identifier or a serial device path. + */ + public var path: String + public var transport: JadeTransportKind + /** + * Advertised or descriptor name, for example "Jade C0FFEE". + */ + public var name: String? + public var serialNumber: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, state: CJitStateEnum, feeSat: UInt64, networkFeeSat: UInt64, serviceFeeSat: UInt64, channelSizeSat: UInt64, channelExpiryWeeks: UInt32, channelOpenError: String?, nodeId: String, invoice: IBtBolt11Invoice, channel: IBtChannel?, lspNode: ILspNode, couponCode: String, source: String?, discount: IDiscount?, expiresAt: String, updatedAt: String, createdAt: String) { - self.id = id - self.state = state - self.feeSat = feeSat - self.networkFeeSat = networkFeeSat - self.serviceFeeSat = serviceFeeSat - self.channelSizeSat = channelSizeSat - self.channelExpiryWeeks = channelExpiryWeeks - self.channelOpenError = channelOpenError - self.nodeId = nodeId - self.invoice = invoice - self.channel = channel - self.lspNode = lspNode - self.couponCode = couponCode - self.source = source - self.discount = discount - self.expiresAt = expiresAt - self.updatedAt = updatedAt - self.createdAt = createdAt + public init( + /** + * Transport specific address: a BLE identifier or a serial device path. + */path: String, transport: JadeTransportKind, + /** + * Advertised or descriptor name, for example "Jade C0FFEE". + */name: String?, serialNumber: String?) { + self.path = path + self.transport = transport + self.name = name + self.serialNumber = serialNumber } } #if compiler(>=6) -extension IcJitEntry: Sendable {} +extension JadeNativeDevice: Sendable {} #endif -extension IcJitEntry: Equatable, Hashable { - public static func ==(lhs: IcJitEntry, rhs: IcJitEntry) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.state != rhs.state { - return false - } - if lhs.feeSat != rhs.feeSat { - return false - } - if lhs.networkFeeSat != rhs.networkFeeSat { - return false - } - if lhs.serviceFeeSat != rhs.serviceFeeSat { - return false - } - if lhs.channelSizeSat != rhs.channelSizeSat { - return false - } - if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { - return false - } - if lhs.channelOpenError != rhs.channelOpenError { - return false - } - if lhs.nodeId != rhs.nodeId { - return false - } - if lhs.invoice != rhs.invoice { - return false - } - if lhs.channel != rhs.channel { - return false - } - if lhs.lspNode != rhs.lspNode { - return false - } - if lhs.couponCode != rhs.couponCode { - return false - } - if lhs.source != rhs.source { - return false - } - if lhs.discount != rhs.discount { +extension JadeNativeDevice: Equatable, Hashable { + public static func ==(lhs: JadeNativeDevice, rhs: JadeNativeDevice) -> Bool { + if lhs.path != rhs.path { return false } - if lhs.expiresAt != rhs.expiresAt { + if lhs.transport != rhs.transport { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.name != rhs.name { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.serialNumber != rhs.serialNumber { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(state) - hasher.combine(feeSat) - hasher.combine(networkFeeSat) - hasher.combine(serviceFeeSat) - hasher.combine(channelSizeSat) - hasher.combine(channelExpiryWeeks) - hasher.combine(channelOpenError) - hasher.combine(nodeId) - hasher.combine(invoice) - hasher.combine(channel) - hasher.combine(lspNode) - hasher.combine(couponCode) - hasher.combine(source) - hasher.combine(discount) - hasher.combine(expiresAt) - hasher.combine(updatedAt) - hasher.combine(createdAt) + hasher.combine(path) + hasher.combine(transport) + hasher.combine(name) + hasher.combine(serialNumber) } } -extension IcJitEntry: Codable {} +extension JadeNativeDevice: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeICJitEntry: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IcJitEntry { +public struct FfiConverterTypeJadeNativeDevice: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNativeDevice { return - try IcJitEntry( - id: FfiConverterString.read(from: &buf), - state: FfiConverterTypeCJitStateEnum.read(from: &buf), - feeSat: FfiConverterUInt64.read(from: &buf), - networkFeeSat: FfiConverterUInt64.read(from: &buf), - serviceFeeSat: FfiConverterUInt64.read(from: &buf), - channelSizeSat: FfiConverterUInt64.read(from: &buf), - channelExpiryWeeks: FfiConverterUInt32.read(from: &buf), - channelOpenError: FfiConverterOptionString.read(from: &buf), - nodeId: FfiConverterString.read(from: &buf), - invoice: FfiConverterTypeIBtBolt11Invoice.read(from: &buf), - channel: FfiConverterOptionTypeIBtChannel.read(from: &buf), - lspNode: FfiConverterTypeILspNode.read(from: &buf), - couponCode: FfiConverterString.read(from: &buf), - source: FfiConverterOptionString.read(from: &buf), - discount: FfiConverterOptionTypeIDiscount.read(from: &buf), - expiresAt: FfiConverterString.read(from: &buf), - updatedAt: FfiConverterString.read(from: &buf), - createdAt: FfiConverterString.read(from: &buf) + try JadeNativeDevice( + path: FfiConverterString.read(from: &buf), + transport: FfiConverterTypeJadeTransportKind.read(from: &buf), + name: FfiConverterOptionString.read(from: &buf), + serialNumber: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: IcJitEntry, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypeCJitStateEnum.write(value.state, into: &buf) - FfiConverterUInt64.write(value.feeSat, into: &buf) - FfiConverterUInt64.write(value.networkFeeSat, into: &buf) - FfiConverterUInt64.write(value.serviceFeeSat, into: &buf) - FfiConverterUInt64.write(value.channelSizeSat, into: &buf) - FfiConverterUInt32.write(value.channelExpiryWeeks, into: &buf) - FfiConverterOptionString.write(value.channelOpenError, into: &buf) - FfiConverterString.write(value.nodeId, into: &buf) - FfiConverterTypeIBtBolt11Invoice.write(value.invoice, into: &buf) - FfiConverterOptionTypeIBtChannel.write(value.channel, into: &buf) - FfiConverterTypeILspNode.write(value.lspNode, into: &buf) - FfiConverterString.write(value.couponCode, into: &buf) - FfiConverterOptionString.write(value.source, into: &buf) - FfiConverterOptionTypeIDiscount.write(value.discount, into: &buf) - FfiConverterString.write(value.expiresAt, into: &buf) - FfiConverterString.write(value.updatedAt, into: &buf) - FfiConverterString.write(value.createdAt, into: &buf) + public static func write(_ value: JadeNativeDevice, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterTypeJadeTransportKind.write(value.transport, into: &buf) + FfiConverterOptionString.write(value.name, into: &buf) + FfiConverterOptionString.write(value.serialNumber, into: &buf) } } @@ -6755,87 +8913,79 @@ public struct FfiConverterTypeICJitEntry: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeICJitEntry_lift(_ buf: RustBuffer) throws -> IcJitEntry { - return try FfiConverterTypeICJitEntry.lift(buf) +public func FfiConverterTypeJadeNativeDevice_lift(_ buf: RustBuffer) throws -> JadeNativeDevice { + return try FfiConverterTypeJadeNativeDevice.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeICJitEntry_lower(_ value: IcJitEntry) -> RustBuffer { - return FfiConverterTypeICJitEntry.lower(value) +public func FfiConverterTypeJadeNativeDevice_lower(_ value: JadeNativeDevice) -> RustBuffer { + return FfiConverterTypeJadeNativeDevice.lower(value) } -public struct IDiscount { - public var code: String - public var absoluteSat: UInt64 - public var relative: Double - public var overallSat: UInt64 +public struct JadeSignedMessage { + public var signature: String + public var address: String + public var derivationPath: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(code: String, absoluteSat: UInt64, relative: Double, overallSat: UInt64) { - self.code = code - self.absoluteSat = absoluteSat - self.relative = relative - self.overallSat = overallSat + public init(signature: String, address: String, derivationPath: String) { + self.signature = signature + self.address = address + self.derivationPath = derivationPath } } #if compiler(>=6) -extension IDiscount: Sendable {} +extension JadeSignedMessage: Sendable {} #endif -extension IDiscount: Equatable, Hashable { - public static func ==(lhs: IDiscount, rhs: IDiscount) -> Bool { - if lhs.code != rhs.code { - return false - } - if lhs.absoluteSat != rhs.absoluteSat { +extension JadeSignedMessage: Equatable, Hashable { + public static func ==(lhs: JadeSignedMessage, rhs: JadeSignedMessage) -> Bool { + if lhs.signature != rhs.signature { return false } - if lhs.relative != rhs.relative { + if lhs.address != rhs.address { return false } - if lhs.overallSat != rhs.overallSat { + if lhs.derivationPath != rhs.derivationPath { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(code) - hasher.combine(absoluteSat) - hasher.combine(relative) - hasher.combine(overallSat) + hasher.combine(signature) + hasher.combine(address) + hasher.combine(derivationPath) } } -extension IDiscount: Codable {} +extension JadeSignedMessage: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIDiscount: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IDiscount { +public struct FfiConverterTypeJadeSignedMessage: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeSignedMessage { return - try IDiscount( - code: FfiConverterString.read(from: &buf), - absoluteSat: FfiConverterUInt64.read(from: &buf), - relative: FfiConverterDouble.read(from: &buf), - overallSat: FfiConverterUInt64.read(from: &buf) + try JadeSignedMessage( + signature: FfiConverterString.read(from: &buf), + address: FfiConverterString.read(from: &buf), + derivationPath: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IDiscount, into buf: inout [UInt8]) { - FfiConverterString.write(value.code, into: &buf) - FfiConverterUInt64.write(value.absoluteSat, into: &buf) - FfiConverterDouble.write(value.relative, into: &buf) - FfiConverterUInt64.write(value.overallSat, into: &buf) + public static func write(_ value: JadeSignedMessage, into buf: inout [UInt8]) { + FfiConverterString.write(value.signature, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.derivationPath, into: &buf) } } @@ -6843,135 +8993,104 @@ public struct FfiConverterTypeIDiscount: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIDiscount_lift(_ buf: RustBuffer) throws -> IDiscount { - return try FfiConverterTypeIDiscount.lift(buf) +public func FfiConverterTypeJadeSignedMessage_lift(_ buf: RustBuffer) throws -> JadeSignedMessage { + return try FfiConverterTypeJadeSignedMessage.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIDiscount_lower(_ value: IDiscount) -> RustBuffer { - return FfiConverterTypeIDiscount.lower(value) +public func FfiConverterTypeJadeSignedMessage_lower(_ value: JadeSignedMessage) -> RustBuffer { + return FfiConverterTypeJadeSignedMessage.lower(value) } -public struct IGift { - public var id: String - public var nodeId: String - public var orderId: String? - public var order: IGiftOrder? - public var bolt11PaymentId: String? - public var bolt11Payment: IGiftPayment? - public var appliedGiftCodeId: String? - public var appliedGiftCode: IGiftCode? - public var createdAt: String? - public var updatedAt: String? +/** + * Outcome of a read. + */ +public struct JadeTransportReadResult { + public var success: Bool + /** + * Bytes read. Success with an empty vector means nothing has arrived yet, + * which is the normal case while the user is deciding on the device. + */ + public var data: Data + /** + * Empty on success. + */ + public var error: String + public var errorCode: JadeTransportErrorCode? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, nodeId: String, orderId: String?, order: IGiftOrder?, bolt11PaymentId: String?, bolt11Payment: IGiftPayment?, appliedGiftCodeId: String?, appliedGiftCode: IGiftCode?, createdAt: String?, updatedAt: String?) { - self.id = id - self.nodeId = nodeId - self.orderId = orderId - self.order = order - self.bolt11PaymentId = bolt11PaymentId - self.bolt11Payment = bolt11Payment - self.appliedGiftCodeId = appliedGiftCodeId - self.appliedGiftCode = appliedGiftCode - self.createdAt = createdAt - self.updatedAt = updatedAt + public init(success: Bool, + /** + * Bytes read. Success with an empty vector means nothing has arrived yet, + * which is the normal case while the user is deciding on the device. + */data: Data, + /** + * Empty on success. + */error: String, errorCode: JadeTransportErrorCode?) { + self.success = success + self.data = data + self.error = error + self.errorCode = errorCode } } #if compiler(>=6) -extension IGift: Sendable {} +extension JadeTransportReadResult: Sendable {} #endif -extension IGift: Equatable, Hashable { - public static func ==(lhs: IGift, rhs: IGift) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.nodeId != rhs.nodeId { - return false - } - if lhs.orderId != rhs.orderId { - return false - } - if lhs.order != rhs.order { - return false - } - if lhs.bolt11PaymentId != rhs.bolt11PaymentId { - return false - } - if lhs.bolt11Payment != rhs.bolt11Payment { - return false - } - if lhs.appliedGiftCodeId != rhs.appliedGiftCodeId { +extension JadeTransportReadResult: Equatable, Hashable { + public static func ==(lhs: JadeTransportReadResult, rhs: JadeTransportReadResult) -> Bool { + if lhs.success != rhs.success { return false } - if lhs.appliedGiftCode != rhs.appliedGiftCode { + if lhs.data != rhs.data { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.error != rhs.error { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.errorCode != rhs.errorCode { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(nodeId) - hasher.combine(orderId) - hasher.combine(order) - hasher.combine(bolt11PaymentId) - hasher.combine(bolt11Payment) - hasher.combine(appliedGiftCodeId) - hasher.combine(appliedGiftCode) - hasher.combine(createdAt) - hasher.combine(updatedAt) + hasher.combine(success) + hasher.combine(data) + hasher.combine(error) + hasher.combine(errorCode) } } -extension IGift: Codable {} +extension JadeTransportReadResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGift: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGift { +public struct FfiConverterTypeJadeTransportReadResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportReadResult { return - try IGift( - id: FfiConverterString.read(from: &buf), - nodeId: FfiConverterString.read(from: &buf), - orderId: FfiConverterOptionString.read(from: &buf), - order: FfiConverterOptionTypeIGiftOrder.read(from: &buf), - bolt11PaymentId: FfiConverterOptionString.read(from: &buf), - bolt11Payment: FfiConverterOptionTypeIGiftPayment.read(from: &buf), - appliedGiftCodeId: FfiConverterOptionString.read(from: &buf), - appliedGiftCode: FfiConverterOptionTypeIGiftCode.read(from: &buf), - createdAt: FfiConverterOptionString.read(from: &buf), - updatedAt: FfiConverterOptionString.read(from: &buf) + try JadeTransportReadResult( + success: FfiConverterBool.read(from: &buf), + data: FfiConverterData.read(from: &buf), + error: FfiConverterString.read(from: &buf), + errorCode: FfiConverterOptionTypeJadeTransportErrorCode.read(from: &buf) ) } - public static func write(_ value: IGift, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.nodeId, into: &buf) - FfiConverterOptionString.write(value.orderId, into: &buf) - FfiConverterOptionTypeIGiftOrder.write(value.order, into: &buf) - FfiConverterOptionString.write(value.bolt11PaymentId, into: &buf) - FfiConverterOptionTypeIGiftPayment.write(value.bolt11Payment, into: &buf) - FfiConverterOptionString.write(value.appliedGiftCodeId, into: &buf) - FfiConverterOptionTypeIGiftCode.write(value.appliedGiftCode, into: &buf) - FfiConverterOptionString.write(value.createdAt, into: &buf) - FfiConverterOptionString.write(value.updatedAt, into: &buf) + public static func write(_ value: JadeTransportReadResult, into buf: inout [UInt8]) { + FfiConverterBool.write(value.success, into: &buf) + FfiConverterData.write(value.data, into: &buf) + FfiConverterString.write(value.error, into: &buf) + FfiConverterOptionTypeJadeTransportErrorCode.write(value.errorCode, into: &buf) } } @@ -6979,143 +9098,88 @@ public struct FfiConverterTypeIGift: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGift_lift(_ buf: RustBuffer) throws -> IGift { - return try FfiConverterTypeIGift.lift(buf) +public func FfiConverterTypeJadeTransportReadResult_lift(_ buf: RustBuffer) throws -> JadeTransportReadResult { + return try FfiConverterTypeJadeTransportReadResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGift_lower(_ value: IGift) -> RustBuffer { - return FfiConverterTypeIGift.lower(value) -} - - -public struct IGiftBolt11Invoice { - public var id: String - public var request: String - public var state: String - public var isHodlInvoice: Bool? - public var paymentHash: String? - public var amountSat: UInt64? - public var amountMsat: String? - public var internalNodePubkey: String? - public var updatedAt: String? - public var createdAt: String? - public var expiresAt: String? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(id: String, request: String, state: String, isHodlInvoice: Bool?, paymentHash: String?, amountSat: UInt64?, amountMsat: String?, internalNodePubkey: String?, updatedAt: String?, createdAt: String?, expiresAt: String?) { - self.id = id - self.request = request - self.state = state - self.isHodlInvoice = isHodlInvoice - self.paymentHash = paymentHash - self.amountSat = amountSat - self.amountMsat = amountMsat - self.internalNodePubkey = internalNodePubkey - self.updatedAt = updatedAt - self.createdAt = createdAt - self.expiresAt = expiresAt - } +public func FfiConverterTypeJadeTransportReadResult_lower(_ value: JadeTransportReadResult) -> RustBuffer { + return FfiConverterTypeJadeTransportReadResult.lower(value) } -#if compiler(>=6) -extension IGiftBolt11Invoice: Sendable {} -#endif - -extension IGiftBolt11Invoice: Equatable, Hashable { - public static func ==(lhs: IGiftBolt11Invoice, rhs: IGiftBolt11Invoice) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.request != rhs.request { - return false - } - if lhs.state != rhs.state { - return false - } - if lhs.isHodlInvoice != rhs.isHodlInvoice { - return false - } - if lhs.paymentHash != rhs.paymentHash { - return false - } - if lhs.amountSat != rhs.amountSat { - return false - } - if lhs.amountMsat != rhs.amountMsat { - return false - } - if lhs.internalNodePubkey != rhs.internalNodePubkey { - return false - } - if lhs.updatedAt != rhs.updatedAt { +/** + * Outcome of an operation that returns no data. + */ +public struct JadeTransportResult { + public var success: Bool + /** + * Empty on success. + */ + public var error: String + public var errorCode: JadeTransportErrorCode? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(success: Bool, + /** + * Empty on success. + */error: String, errorCode: JadeTransportErrorCode?) { + self.success = success + self.error = error + self.errorCode = errorCode + } +} + +#if compiler(>=6) +extension JadeTransportResult: Sendable {} +#endif + + +extension JadeTransportResult: Equatable, Hashable { + public static func ==(lhs: JadeTransportResult, rhs: JadeTransportResult) -> Bool { + if lhs.success != rhs.success { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.error != rhs.error { return false } - if lhs.expiresAt != rhs.expiresAt { + if lhs.errorCode != rhs.errorCode { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(request) - hasher.combine(state) - hasher.combine(isHodlInvoice) - hasher.combine(paymentHash) - hasher.combine(amountSat) - hasher.combine(amountMsat) - hasher.combine(internalNodePubkey) - hasher.combine(updatedAt) - hasher.combine(createdAt) - hasher.combine(expiresAt) + hasher.combine(success) + hasher.combine(error) + hasher.combine(errorCode) } } -extension IGiftBolt11Invoice: Codable {} +extension JadeTransportResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftBolt11Invoice: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftBolt11Invoice { +public struct FfiConverterTypeJadeTransportResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportResult { return - try IGiftBolt11Invoice( - id: FfiConverterString.read(from: &buf), - request: FfiConverterString.read(from: &buf), - state: FfiConverterString.read(from: &buf), - isHodlInvoice: FfiConverterOptionBool.read(from: &buf), - paymentHash: FfiConverterOptionString.read(from: &buf), - amountSat: FfiConverterOptionUInt64.read(from: &buf), - amountMsat: FfiConverterOptionString.read(from: &buf), - internalNodePubkey: FfiConverterOptionString.read(from: &buf), - updatedAt: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterOptionString.read(from: &buf), - expiresAt: FfiConverterOptionString.read(from: &buf) + try JadeTransportResult( + success: FfiConverterBool.read(from: &buf), + error: FfiConverterString.read(from: &buf), + errorCode: FfiConverterOptionTypeJadeTransportErrorCode.read(from: &buf) ) } - public static func write(_ value: IGiftBolt11Invoice, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.request, into: &buf) - FfiConverterString.write(value.state, into: &buf) - FfiConverterOptionBool.write(value.isHodlInvoice, into: &buf) - FfiConverterOptionString.write(value.paymentHash, into: &buf) - FfiConverterOptionUInt64.write(value.amountSat, into: &buf) - FfiConverterOptionString.write(value.amountMsat, into: &buf) - FfiConverterOptionString.write(value.internalNodePubkey, into: &buf) - FfiConverterOptionString.write(value.updatedAt, into: &buf) - FfiConverterOptionString.write(value.createdAt, into: &buf) - FfiConverterOptionString.write(value.expiresAt, into: &buf) + public static func write(_ value: JadeTransportResult, into buf: inout [UInt8]) { + FfiConverterBool.write(value.success, into: &buf) + FfiConverterString.write(value.error, into: &buf) + FfiConverterOptionTypeJadeTransportErrorCode.write(value.errorCode, into: &buf) } } @@ -7123,127 +9187,151 @@ public struct FfiConverterTypeIGiftBolt11Invoice: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftBolt11Invoice_lift(_ buf: RustBuffer) throws -> IGiftBolt11Invoice { - return try FfiConverterTypeIGiftBolt11Invoice.lift(buf) +public func FfiConverterTypeJadeTransportResult_lift(_ buf: RustBuffer) throws -> JadeTransportResult { + return try FfiConverterTypeJadeTransportResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftBolt11Invoice_lower(_ value: IGiftBolt11Invoice) -> RustBuffer { - return FfiConverterTypeIGiftBolt11Invoice.lower(value) +public func FfiConverterTypeJadeTransportResult_lower(_ value: JadeTransportResult) -> RustBuffer { + return FfiConverterTypeJadeTransportResult.lower(value) } -public struct IGiftBtcAddress { - public var id: String - public var address: String - public var transactions: [String] - public var allTransactions: [String] - public var isBlacklisted: Bool? - public var watchUntil: String? - public var watchForBlockConfirmations: UInt32? - public var updatedAt: String? - public var createdAt: String? +public struct JadeVersionInfo { + public var jadeVersion: String + public var jadeState: JadeState + public var jadeNetworks: String? + public var jadeHasPin: Bool? + public var boardType: String? + public var jadeConfig: String? + public var jadeFeatures: String? + public var idfVersion: String? + public var chipFeatures: String? + public var efuseMac: String? + public var batteryStatus: UInt32? + public var jadeOtaMaxChunk: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, address: String, transactions: [String], allTransactions: [String], isBlacklisted: Bool?, watchUntil: String?, watchForBlockConfirmations: UInt32?, updatedAt: String?, createdAt: String?) { - self.id = id - self.address = address - self.transactions = transactions - self.allTransactions = allTransactions - self.isBlacklisted = isBlacklisted - self.watchUntil = watchUntil - self.watchForBlockConfirmations = watchForBlockConfirmations - self.updatedAt = updatedAt - self.createdAt = createdAt + public init(jadeVersion: String, jadeState: JadeState, jadeNetworks: String?, jadeHasPin: Bool?, boardType: String?, jadeConfig: String?, jadeFeatures: String?, idfVersion: String?, chipFeatures: String?, efuseMac: String?, batteryStatus: UInt32?, jadeOtaMaxChunk: UInt32?) { + self.jadeVersion = jadeVersion + self.jadeState = jadeState + self.jadeNetworks = jadeNetworks + self.jadeHasPin = jadeHasPin + self.boardType = boardType + self.jadeConfig = jadeConfig + self.jadeFeatures = jadeFeatures + self.idfVersion = idfVersion + self.chipFeatures = chipFeatures + self.efuseMac = efuseMac + self.batteryStatus = batteryStatus + self.jadeOtaMaxChunk = jadeOtaMaxChunk } } #if compiler(>=6) -extension IGiftBtcAddress: Sendable {} +extension JadeVersionInfo: Sendable {} #endif -extension IGiftBtcAddress: Equatable, Hashable { - public static func ==(lhs: IGiftBtcAddress, rhs: IGiftBtcAddress) -> Bool { - if lhs.id != rhs.id { +extension JadeVersionInfo: Equatable, Hashable { + public static func ==(lhs: JadeVersionInfo, rhs: JadeVersionInfo) -> Bool { + if lhs.jadeVersion != rhs.jadeVersion { return false } - if lhs.address != rhs.address { + if lhs.jadeState != rhs.jadeState { return false } - if lhs.transactions != rhs.transactions { + if lhs.jadeNetworks != rhs.jadeNetworks { return false } - if lhs.allTransactions != rhs.allTransactions { + if lhs.jadeHasPin != rhs.jadeHasPin { return false } - if lhs.isBlacklisted != rhs.isBlacklisted { + if lhs.boardType != rhs.boardType { return false } - if lhs.watchUntil != rhs.watchUntil { + if lhs.jadeConfig != rhs.jadeConfig { return false } - if lhs.watchForBlockConfirmations != rhs.watchForBlockConfirmations { + if lhs.jadeFeatures != rhs.jadeFeatures { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.idfVersion != rhs.idfVersion { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.chipFeatures != rhs.chipFeatures { + return false + } + if lhs.efuseMac != rhs.efuseMac { + return false + } + if lhs.batteryStatus != rhs.batteryStatus { + return false + } + if lhs.jadeOtaMaxChunk != rhs.jadeOtaMaxChunk { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(address) - hasher.combine(transactions) - hasher.combine(allTransactions) - hasher.combine(isBlacklisted) - hasher.combine(watchUntil) - hasher.combine(watchForBlockConfirmations) - hasher.combine(updatedAt) - hasher.combine(createdAt) + hasher.combine(jadeVersion) + hasher.combine(jadeState) + hasher.combine(jadeNetworks) + hasher.combine(jadeHasPin) + hasher.combine(boardType) + hasher.combine(jadeConfig) + hasher.combine(jadeFeatures) + hasher.combine(idfVersion) + hasher.combine(chipFeatures) + hasher.combine(efuseMac) + hasher.combine(batteryStatus) + hasher.combine(jadeOtaMaxChunk) } } -extension IGiftBtcAddress: Codable {} +extension JadeVersionInfo: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftBtcAddress: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftBtcAddress { +public struct FfiConverterTypeJadeVersionInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeVersionInfo { return - try IGiftBtcAddress( - id: FfiConverterString.read(from: &buf), - address: FfiConverterString.read(from: &buf), - transactions: FfiConverterSequenceString.read(from: &buf), - allTransactions: FfiConverterSequenceString.read(from: &buf), - isBlacklisted: FfiConverterOptionBool.read(from: &buf), - watchUntil: FfiConverterOptionString.read(from: &buf), - watchForBlockConfirmations: FfiConverterOptionUInt32.read(from: &buf), - updatedAt: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterOptionString.read(from: &buf) + try JadeVersionInfo( + jadeVersion: FfiConverterString.read(from: &buf), + jadeState: FfiConverterTypeJadeState.read(from: &buf), + jadeNetworks: FfiConverterOptionString.read(from: &buf), + jadeHasPin: FfiConverterOptionBool.read(from: &buf), + boardType: FfiConverterOptionString.read(from: &buf), + jadeConfig: FfiConverterOptionString.read(from: &buf), + jadeFeatures: FfiConverterOptionString.read(from: &buf), + idfVersion: FfiConverterOptionString.read(from: &buf), + chipFeatures: FfiConverterOptionString.read(from: &buf), + efuseMac: FfiConverterOptionString.read(from: &buf), + batteryStatus: FfiConverterOptionUInt32.read(from: &buf), + jadeOtaMaxChunk: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: IGiftBtcAddress, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.address, into: &buf) - FfiConverterSequenceString.write(value.transactions, into: &buf) - FfiConverterSequenceString.write(value.allTransactions, into: &buf) - FfiConverterOptionBool.write(value.isBlacklisted, into: &buf) - FfiConverterOptionString.write(value.watchUntil, into: &buf) - FfiConverterOptionUInt32.write(value.watchForBlockConfirmations, into: &buf) - FfiConverterOptionString.write(value.updatedAt, into: &buf) - FfiConverterOptionString.write(value.createdAt, into: &buf) + public static func write(_ value: JadeVersionInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.jadeVersion, into: &buf) + FfiConverterTypeJadeState.write(value.jadeState, into: &buf) + FfiConverterOptionString.write(value.jadeNetworks, into: &buf) + FfiConverterOptionBool.write(value.jadeHasPin, into: &buf) + FfiConverterOptionString.write(value.boardType, into: &buf) + FfiConverterOptionString.write(value.jadeConfig, into: &buf) + FfiConverterOptionString.write(value.jadeFeatures, into: &buf) + FfiConverterOptionString.write(value.idfVersion, into: &buf) + FfiConverterOptionString.write(value.chipFeatures, into: &buf) + FfiConverterOptionString.write(value.efuseMac, into: &buf) + FfiConverterOptionUInt32.write(value.batteryStatus, into: &buf) + FfiConverterOptionUInt32.write(value.jadeOtaMaxChunk, into: &buf) } } @@ -7251,119 +9339,79 @@ public struct FfiConverterTypeIGiftBtcAddress: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftBtcAddress_lift(_ buf: RustBuffer) throws -> IGiftBtcAddress { - return try FfiConverterTypeIGiftBtcAddress.lift(buf) +public func FfiConverterTypeJadeVersionInfo_lift(_ buf: RustBuffer) throws -> JadeVersionInfo { + return try FfiConverterTypeJadeVersionInfo.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftBtcAddress_lower(_ value: IGiftBtcAddress) -> RustBuffer { - return FfiConverterTypeIGiftBtcAddress.lower(value) +public func FfiConverterTypeJadeVersionInfo_lower(_ value: JadeVersionInfo) -> RustBuffer { + return FfiConverterTypeJadeVersionInfo.lower(value) } -public struct IGiftCode { - public var id: String - public var code: String - public var createdAt: String - public var updatedAt: String - public var expiresAt: String - public var giftSat: UInt64? - public var scope: String? - public var maxCount: UInt32? +public struct JadeXpubResponse { + public var xpub: String + public var derivationPath: String + public var masterFingerprint: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, code: String, createdAt: String, updatedAt: String, expiresAt: String, giftSat: UInt64?, scope: String?, maxCount: UInt32?) { - self.id = id - self.code = code - self.createdAt = createdAt - self.updatedAt = updatedAt - self.expiresAt = expiresAt - self.giftSat = giftSat - self.scope = scope - self.maxCount = maxCount + public init(xpub: String, derivationPath: String, masterFingerprint: String) { + self.xpub = xpub + self.derivationPath = derivationPath + self.masterFingerprint = masterFingerprint } } #if compiler(>=6) -extension IGiftCode: Sendable {} +extension JadeXpubResponse: Sendable {} #endif -extension IGiftCode: Equatable, Hashable { - public static func ==(lhs: IGiftCode, rhs: IGiftCode) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.code != rhs.code { - return false - } - if lhs.createdAt != rhs.createdAt { - return false - } - if lhs.updatedAt != rhs.updatedAt { - return false - } - if lhs.expiresAt != rhs.expiresAt { - return false - } - if lhs.giftSat != rhs.giftSat { +extension JadeXpubResponse: Equatable, Hashable { + public static func ==(lhs: JadeXpubResponse, rhs: JadeXpubResponse) -> Bool { + if lhs.xpub != rhs.xpub { return false } - if lhs.scope != rhs.scope { + if lhs.derivationPath != rhs.derivationPath { return false } - if lhs.maxCount != rhs.maxCount { + if lhs.masterFingerprint != rhs.masterFingerprint { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(code) - hasher.combine(createdAt) - hasher.combine(updatedAt) - hasher.combine(expiresAt) - hasher.combine(giftSat) - hasher.combine(scope) - hasher.combine(maxCount) + hasher.combine(xpub) + hasher.combine(derivationPath) + hasher.combine(masterFingerprint) } } -extension IGiftCode: Codable {} +extension JadeXpubResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftCode: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftCode { +public struct FfiConverterTypeJadeXpubResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeXpubResponse { return - try IGiftCode( - id: FfiConverterString.read(from: &buf), - code: FfiConverterString.read(from: &buf), - createdAt: FfiConverterString.read(from: &buf), - updatedAt: FfiConverterString.read(from: &buf), - expiresAt: FfiConverterString.read(from: &buf), - giftSat: FfiConverterOptionUInt64.read(from: &buf), - scope: FfiConverterOptionString.read(from: &buf), - maxCount: FfiConverterOptionUInt32.read(from: &buf) + try JadeXpubResponse( + xpub: FfiConverterString.read(from: &buf), + derivationPath: FfiConverterString.read(from: &buf), + masterFingerprint: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IGiftCode, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.code, into: &buf) - FfiConverterString.write(value.createdAt, into: &buf) - FfiConverterString.write(value.updatedAt, into: &buf) - FfiConverterString.write(value.expiresAt, into: &buf) - FfiConverterOptionUInt64.write(value.giftSat, into: &buf) - FfiConverterOptionString.write(value.scope, into: &buf) - FfiConverterOptionUInt32.write(value.maxCount, into: &buf) + public static func write(_ value: JadeXpubResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.xpub, into: &buf) + FfiConverterString.write(value.derivationPath, into: &buf) + FfiConverterString.write(value.masterFingerprint, into: &buf) } } @@ -7371,79 +9419,83 @@ public struct FfiConverterTypeIGiftCode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftCode_lift(_ buf: RustBuffer) throws -> IGiftCode { - return try FfiConverterTypeIGiftCode.lift(buf) +public func FfiConverterTypeJadeXpubResponse_lift(_ buf: RustBuffer) throws -> JadeXpubResponse { + return try FfiConverterTypeJadeXpubResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftCode_lower(_ value: IGiftCode) -> RustBuffer { - return FfiConverterTypeIGiftCode.lower(value) +public func FfiConverterTypeJadeXpubResponse_lower(_ value: JadeXpubResponse) -> RustBuffer { + return FfiConverterTypeJadeXpubResponse.lower(value) } -public struct IGiftLspNode { - public var alias: String - public var pubkey: String - public var connectionStrings: [String] +public struct LegacyRnCloseRecoveryScanResult { + /** + * Total balance found in legacy RN P2WPKH close outputs (in satoshis). + */ + public var totalAmount: UInt64 + /** + * Number of P2WPKH outputs found. + */ + public var outputsCount: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(alias: String, pubkey: String, connectionStrings: [String]) { - self.alias = alias - self.pubkey = pubkey - self.connectionStrings = connectionStrings + public init( + /** + * Total balance found in legacy RN P2WPKH close outputs (in satoshis). + */totalAmount: UInt64, + /** + * Number of P2WPKH outputs found. + */outputsCount: UInt32) { + self.totalAmount = totalAmount + self.outputsCount = outputsCount } } #if compiler(>=6) -extension IGiftLspNode: Sendable {} +extension LegacyRnCloseRecoveryScanResult: Sendable {} #endif -extension IGiftLspNode: Equatable, Hashable { - public static func ==(lhs: IGiftLspNode, rhs: IGiftLspNode) -> Bool { - if lhs.alias != rhs.alias { - return false - } - if lhs.pubkey != rhs.pubkey { +extension LegacyRnCloseRecoveryScanResult: Equatable, Hashable { + public static func ==(lhs: LegacyRnCloseRecoveryScanResult, rhs: LegacyRnCloseRecoveryScanResult) -> Bool { + if lhs.totalAmount != rhs.totalAmount { return false } - if lhs.connectionStrings != rhs.connectionStrings { + if lhs.outputsCount != rhs.outputsCount { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(alias) - hasher.combine(pubkey) - hasher.combine(connectionStrings) + hasher.combine(totalAmount) + hasher.combine(outputsCount) } } -extension IGiftLspNode: Codable {} +extension LegacyRnCloseRecoveryScanResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftLspNode: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftLspNode { +public struct FfiConverterTypeLegacyRnCloseRecoveryScanResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LegacyRnCloseRecoveryScanResult { return - try IGiftLspNode( - alias: FfiConverterString.read(from: &buf), - pubkey: FfiConverterString.read(from: &buf), - connectionStrings: FfiConverterSequenceString.read(from: &buf) + try LegacyRnCloseRecoveryScanResult( + totalAmount: FfiConverterUInt64.read(from: &buf), + outputsCount: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: IGiftLspNode, into buf: inout [UInt8]) { - FfiConverterString.write(value.alias, into: &buf) - FfiConverterString.write(value.pubkey, into: &buf) - FfiConverterSequenceString.write(value.connectionStrings, into: &buf) + public static func write(_ value: LegacyRnCloseRecoveryScanResult, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.totalAmount, into: &buf) + FfiConverterUInt32.write(value.outputsCount, into: &buf) } } @@ -7451,231 +9503,167 @@ public struct FfiConverterTypeIGiftLspNode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftLspNode_lift(_ buf: RustBuffer) throws -> IGiftLspNode { - return try FfiConverterTypeIGiftLspNode.lift(buf) +public func FfiConverterTypeLegacyRnCloseRecoveryScanResult_lift(_ buf: RustBuffer) throws -> LegacyRnCloseRecoveryScanResult { + return try FfiConverterTypeLegacyRnCloseRecoveryScanResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftLspNode_lower(_ value: IGiftLspNode) -> RustBuffer { - return FfiConverterTypeIGiftLspNode.lower(value) +public func FfiConverterTypeLegacyRnCloseRecoveryScanResult_lower(_ value: LegacyRnCloseRecoveryScanResult) -> RustBuffer { + return FfiConverterTypeLegacyRnCloseRecoveryScanResult.lower(value) } -public struct IGiftOrder { - public var id: String - public var state: String - public var oldState: String? - public var isChannelExpired: Bool? - public var isOrderExpired: Bool? - public var lspBalanceSat: UInt64? - public var clientBalanceSat: UInt64? - public var channelExpiryWeeks: UInt32? - public var zeroConf: Bool? - public var zeroReserve: Bool? - public var announced: Bool? - public var clientNodeId: String? - public var channelExpiresAt: String? - public var orderExpiresAt: String? - public var feeSat: UInt64? - public var networkFeeSat: UInt64? - public var serviceFeeSat: UInt64? - public var payment: IGiftPayment? - public var lspNode: IGiftLspNode? - public var updatedAt: String? - public var createdAt: String? - public var nodeIdVerified: Bool? +public struct LegacyRnCloseRecoverySweepPreview { + /** + * Fully signed raw sweep transaction hex. Broadcast only after user confirmation. + */ + public var txHex: String + /** + * Transaction id of the sweep transaction. + */ + public var txid: String + /** + * Total input amount in satoshis. + */ + public var totalAmount: UInt64 + /** + * Fee in satoshis. + */ + public var estimatedFee: UInt64 + /** + * Transaction virtual size in vbytes. + */ + public var estimatedVsize: UInt64 + /** + * Number of recovered outputs swept. + */ + public var outputsCount: UInt32 + /** + * Destination address receiving the sweep. + */ + public var destinationAddress: String + /** + * Amount sent to destination after fees. + */ + public var amountAfterFees: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, state: String, oldState: String?, isChannelExpired: Bool?, isOrderExpired: Bool?, lspBalanceSat: UInt64?, clientBalanceSat: UInt64?, channelExpiryWeeks: UInt32?, zeroConf: Bool?, zeroReserve: Bool?, announced: Bool?, clientNodeId: String?, channelExpiresAt: String?, orderExpiresAt: String?, feeSat: UInt64?, networkFeeSat: UInt64?, serviceFeeSat: UInt64?, payment: IGiftPayment?, lspNode: IGiftLspNode?, updatedAt: String?, createdAt: String?, nodeIdVerified: Bool?) { - self.id = id - self.state = state - self.oldState = oldState - self.isChannelExpired = isChannelExpired - self.isOrderExpired = isOrderExpired - self.lspBalanceSat = lspBalanceSat - self.clientBalanceSat = clientBalanceSat - self.channelExpiryWeeks = channelExpiryWeeks - self.zeroConf = zeroConf - self.zeroReserve = zeroReserve - self.announced = announced - self.clientNodeId = clientNodeId - self.channelExpiresAt = channelExpiresAt - self.orderExpiresAt = orderExpiresAt - self.feeSat = feeSat - self.networkFeeSat = networkFeeSat - self.serviceFeeSat = serviceFeeSat - self.payment = payment - self.lspNode = lspNode - self.updatedAt = updatedAt - self.createdAt = createdAt - self.nodeIdVerified = nodeIdVerified + public init( + /** + * Fully signed raw sweep transaction hex. Broadcast only after user confirmation. + */txHex: String, + /** + * Transaction id of the sweep transaction. + */txid: String, + /** + * Total input amount in satoshis. + */totalAmount: UInt64, + /** + * Fee in satoshis. + */estimatedFee: UInt64, + /** + * Transaction virtual size in vbytes. + */estimatedVsize: UInt64, + /** + * Number of recovered outputs swept. + */outputsCount: UInt32, + /** + * Destination address receiving the sweep. + */destinationAddress: String, + /** + * Amount sent to destination after fees. + */amountAfterFees: UInt64) { + self.txHex = txHex + self.txid = txid + self.totalAmount = totalAmount + self.estimatedFee = estimatedFee + self.estimatedVsize = estimatedVsize + self.outputsCount = outputsCount + self.destinationAddress = destinationAddress + self.amountAfterFees = amountAfterFees } } #if compiler(>=6) -extension IGiftOrder: Sendable {} +extension LegacyRnCloseRecoverySweepPreview: Sendable {} #endif -extension IGiftOrder: Equatable, Hashable { - public static func ==(lhs: IGiftOrder, rhs: IGiftOrder) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.state != rhs.state { - return false - } - if lhs.oldState != rhs.oldState { - return false - } - if lhs.isChannelExpired != rhs.isChannelExpired { - return false - } - if lhs.isOrderExpired != rhs.isOrderExpired { - return false - } - if lhs.lspBalanceSat != rhs.lspBalanceSat { - return false - } - if lhs.clientBalanceSat != rhs.clientBalanceSat { - return false - } - if lhs.channelExpiryWeeks != rhs.channelExpiryWeeks { - return false - } - if lhs.zeroConf != rhs.zeroConf { - return false - } - if lhs.zeroReserve != rhs.zeroReserve { - return false - } - if lhs.announced != rhs.announced { - return false - } - if lhs.clientNodeId != rhs.clientNodeId { - return false - } - if lhs.channelExpiresAt != rhs.channelExpiresAt { - return false - } - if lhs.orderExpiresAt != rhs.orderExpiresAt { - return false - } - if lhs.feeSat != rhs.feeSat { +extension LegacyRnCloseRecoverySweepPreview: Equatable, Hashable { + public static func ==(lhs: LegacyRnCloseRecoverySweepPreview, rhs: LegacyRnCloseRecoverySweepPreview) -> Bool { + if lhs.txHex != rhs.txHex { return false } - if lhs.networkFeeSat != rhs.networkFeeSat { + if lhs.txid != rhs.txid { return false } - if lhs.serviceFeeSat != rhs.serviceFeeSat { + if lhs.totalAmount != rhs.totalAmount { return false } - if lhs.payment != rhs.payment { + if lhs.estimatedFee != rhs.estimatedFee { return false } - if lhs.lspNode != rhs.lspNode { + if lhs.estimatedVsize != rhs.estimatedVsize { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.outputsCount != rhs.outputsCount { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.destinationAddress != rhs.destinationAddress { return false } - if lhs.nodeIdVerified != rhs.nodeIdVerified { + if lhs.amountAfterFees != rhs.amountAfterFees { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(state) - hasher.combine(oldState) - hasher.combine(isChannelExpired) - hasher.combine(isOrderExpired) - hasher.combine(lspBalanceSat) - hasher.combine(clientBalanceSat) - hasher.combine(channelExpiryWeeks) - hasher.combine(zeroConf) - hasher.combine(zeroReserve) - hasher.combine(announced) - hasher.combine(clientNodeId) - hasher.combine(channelExpiresAt) - hasher.combine(orderExpiresAt) - hasher.combine(feeSat) - hasher.combine(networkFeeSat) - hasher.combine(serviceFeeSat) - hasher.combine(payment) - hasher.combine(lspNode) - hasher.combine(updatedAt) - hasher.combine(createdAt) - hasher.combine(nodeIdVerified) + hasher.combine(txHex) + hasher.combine(txid) + hasher.combine(totalAmount) + hasher.combine(estimatedFee) + hasher.combine(estimatedVsize) + hasher.combine(outputsCount) + hasher.combine(destinationAddress) + hasher.combine(amountAfterFees) } } -extension IGiftOrder: Codable {} +extension LegacyRnCloseRecoverySweepPreview: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftOrder: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftOrder { +public struct FfiConverterTypeLegacyRnCloseRecoverySweepPreview: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LegacyRnCloseRecoverySweepPreview { return - try IGiftOrder( - id: FfiConverterString.read(from: &buf), - state: FfiConverterString.read(from: &buf), - oldState: FfiConverterOptionString.read(from: &buf), - isChannelExpired: FfiConverterOptionBool.read(from: &buf), - isOrderExpired: FfiConverterOptionBool.read(from: &buf), - lspBalanceSat: FfiConverterOptionUInt64.read(from: &buf), - clientBalanceSat: FfiConverterOptionUInt64.read(from: &buf), - channelExpiryWeeks: FfiConverterOptionUInt32.read(from: &buf), - zeroConf: FfiConverterOptionBool.read(from: &buf), - zeroReserve: FfiConverterOptionBool.read(from: &buf), - announced: FfiConverterOptionBool.read(from: &buf), - clientNodeId: FfiConverterOptionString.read(from: &buf), - channelExpiresAt: FfiConverterOptionString.read(from: &buf), - orderExpiresAt: FfiConverterOptionString.read(from: &buf), - feeSat: FfiConverterOptionUInt64.read(from: &buf), - networkFeeSat: FfiConverterOptionUInt64.read(from: &buf), - serviceFeeSat: FfiConverterOptionUInt64.read(from: &buf), - payment: FfiConverterOptionTypeIGiftPayment.read(from: &buf), - lspNode: FfiConverterOptionTypeIGiftLspNode.read(from: &buf), - updatedAt: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterOptionString.read(from: &buf), - nodeIdVerified: FfiConverterOptionBool.read(from: &buf) + try LegacyRnCloseRecoverySweepPreview( + txHex: FfiConverterString.read(from: &buf), + txid: FfiConverterString.read(from: &buf), + totalAmount: FfiConverterUInt64.read(from: &buf), + estimatedFee: FfiConverterUInt64.read(from: &buf), + estimatedVsize: FfiConverterUInt64.read(from: &buf), + outputsCount: FfiConverterUInt32.read(from: &buf), + destinationAddress: FfiConverterString.read(from: &buf), + amountAfterFees: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: IGiftOrder, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.state, into: &buf) - FfiConverterOptionString.write(value.oldState, into: &buf) - FfiConverterOptionBool.write(value.isChannelExpired, into: &buf) - FfiConverterOptionBool.write(value.isOrderExpired, into: &buf) - FfiConverterOptionUInt64.write(value.lspBalanceSat, into: &buf) - FfiConverterOptionUInt64.write(value.clientBalanceSat, into: &buf) - FfiConverterOptionUInt32.write(value.channelExpiryWeeks, into: &buf) - FfiConverterOptionBool.write(value.zeroConf, into: &buf) - FfiConverterOptionBool.write(value.zeroReserve, into: &buf) - FfiConverterOptionBool.write(value.announced, into: &buf) - FfiConverterOptionString.write(value.clientNodeId, into: &buf) - FfiConverterOptionString.write(value.channelExpiresAt, into: &buf) - FfiConverterOptionString.write(value.orderExpiresAt, into: &buf) - FfiConverterOptionUInt64.write(value.feeSat, into: &buf) - FfiConverterOptionUInt64.write(value.networkFeeSat, into: &buf) - FfiConverterOptionUInt64.write(value.serviceFeeSat, into: &buf) - FfiConverterOptionTypeIGiftPayment.write(value.payment, into: &buf) - FfiConverterOptionTypeIGiftLspNode.write(value.lspNode, into: &buf) - FfiConverterOptionString.write(value.updatedAt, into: &buf) - FfiConverterOptionString.write(value.createdAt, into: &buf) - FfiConverterOptionBool.write(value.nodeIdVerified, into: &buf) + public static func write(_ value: LegacyRnCloseRecoverySweepPreview, into buf: inout [UInt8]) { + FfiConverterString.write(value.txHex, into: &buf) + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt64.write(value.totalAmount, into: &buf) + FfiConverterUInt64.write(value.estimatedFee, into: &buf) + FfiConverterUInt64.write(value.estimatedVsize, into: &buf) + FfiConverterUInt32.write(value.outputsCount, into: &buf) + FfiConverterString.write(value.destinationAddress, into: &buf) + FfiConverterUInt64.write(value.amountAfterFees, into: &buf) } } @@ -7683,215 +9671,295 @@ public struct FfiConverterTypeIGiftOrder: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftOrder_lift(_ buf: RustBuffer) throws -> IGiftOrder { - return try FfiConverterTypeIGiftOrder.lift(buf) +public func FfiConverterTypeLegacyRnCloseRecoverySweepPreview_lift(_ buf: RustBuffer) throws -> LegacyRnCloseRecoverySweepPreview { + return try FfiConverterTypeLegacyRnCloseRecoverySweepPreview.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftOrder_lower(_ value: IGiftOrder) -> RustBuffer { - return FfiConverterTypeIGiftOrder.lower(value) +public func FfiConverterTypeLegacyRnCloseRecoverySweepPreview_lower(_ value: LegacyRnCloseRecoverySweepPreview) -> RustBuffer { + return FfiConverterTypeLegacyRnCloseRecoverySweepPreview.lower(value) } -public struct IGiftPayment { +public struct LightningActivity { + public var walletId: String public var id: String - public var state: String - public var oldState: String? - public var onchainState: String? - public var lnState: String? - public var paidOnchainSat: UInt64? - public var paidLnSat: UInt64? - public var paidSat: UInt64? - public var isOverpaid: Bool? - public var isRefunded: Bool? - public var overpaidAmountSat: UInt64? - public var requiredOnchainConfirmations: UInt32? - public var settlementState: String? - public var expectedAmountSat: UInt64? - public var isManuallyPaid: Bool? - public var btcAddress: IGiftBtcAddress? - public var btcAddressId: String? - public var bolt11Invoice: IGiftBolt11Invoice? - public var bolt11InvoiceId: String? - public var manualRefunds: [String] + public var txType: PaymentType + public var status: PaymentState + public var value: UInt64 + public var fee: UInt64? + public var invoice: String + public var message: String + public var timestamp: UInt64 + public var preimage: String? + public var contact: String? + public var createdAt: UInt64? + public var updatedAt: UInt64? + public var seenAt: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, state: String, oldState: String?, onchainState: String?, lnState: String?, paidOnchainSat: UInt64?, paidLnSat: UInt64?, paidSat: UInt64?, isOverpaid: Bool?, isRefunded: Bool?, overpaidAmountSat: UInt64?, requiredOnchainConfirmations: UInt32?, settlementState: String?, expectedAmountSat: UInt64?, isManuallyPaid: Bool?, btcAddress: IGiftBtcAddress?, btcAddressId: String?, bolt11Invoice: IGiftBolt11Invoice?, bolt11InvoiceId: String?, manualRefunds: [String]) { + public init(walletId: String, id: String, txType: PaymentType, status: PaymentState, value: UInt64, fee: UInt64?, invoice: String, message: String, timestamp: UInt64, preimage: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { + self.walletId = walletId self.id = id - self.state = state - self.oldState = oldState - self.onchainState = onchainState - self.lnState = lnState - self.paidOnchainSat = paidOnchainSat - self.paidLnSat = paidLnSat - self.paidSat = paidSat - self.isOverpaid = isOverpaid - self.isRefunded = isRefunded - self.overpaidAmountSat = overpaidAmountSat - self.requiredOnchainConfirmations = requiredOnchainConfirmations - self.settlementState = settlementState - self.expectedAmountSat = expectedAmountSat - self.isManuallyPaid = isManuallyPaid - self.btcAddress = btcAddress - self.btcAddressId = btcAddressId - self.bolt11Invoice = bolt11Invoice - self.bolt11InvoiceId = bolt11InvoiceId - self.manualRefunds = manualRefunds + self.txType = txType + self.status = status + self.value = value + self.fee = fee + self.invoice = invoice + self.message = message + self.timestamp = timestamp + self.preimage = preimage + self.contact = contact + self.createdAt = createdAt + self.updatedAt = updatedAt + self.seenAt = seenAt } } #if compiler(>=6) -extension IGiftPayment: Sendable {} +extension LightningActivity: Sendable {} #endif -extension IGiftPayment: Equatable, Hashable { - public static func ==(lhs: IGiftPayment, rhs: IGiftPayment) -> Bool { +extension LightningActivity: Equatable, Hashable { + public static func ==(lhs: LightningActivity, rhs: LightningActivity) -> Bool { + if lhs.walletId != rhs.walletId { + return false + } if lhs.id != rhs.id { return false } - if lhs.state != rhs.state { + if lhs.txType != rhs.txType { return false } - if lhs.oldState != rhs.oldState { + if lhs.status != rhs.status { return false } - if lhs.onchainState != rhs.onchainState { + if lhs.value != rhs.value { return false } - if lhs.lnState != rhs.lnState { + if lhs.fee != rhs.fee { return false } - if lhs.paidOnchainSat != rhs.paidOnchainSat { + if lhs.invoice != rhs.invoice { return false } - if lhs.paidLnSat != rhs.paidLnSat { + if lhs.message != rhs.message { return false } - if lhs.paidSat != rhs.paidSat { + if lhs.timestamp != rhs.timestamp { return false } - if lhs.isOverpaid != rhs.isOverpaid { + if lhs.preimage != rhs.preimage { return false } - if lhs.isRefunded != rhs.isRefunded { + if lhs.contact != rhs.contact { return false } - if lhs.overpaidAmountSat != rhs.overpaidAmountSat { + if lhs.createdAt != rhs.createdAt { return false } - if lhs.requiredOnchainConfirmations != rhs.requiredOnchainConfirmations { + if lhs.updatedAt != rhs.updatedAt { return false } - if lhs.settlementState != rhs.settlementState { + if lhs.seenAt != rhs.seenAt { return false } - if lhs.expectedAmountSat != rhs.expectedAmountSat { + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(walletId) + hasher.combine(id) + hasher.combine(txType) + hasher.combine(status) + hasher.combine(value) + hasher.combine(fee) + hasher.combine(invoice) + hasher.combine(message) + hasher.combine(timestamp) + hasher.combine(preimage) + hasher.combine(contact) + hasher.combine(createdAt) + hasher.combine(updatedAt) + hasher.combine(seenAt) + } +} + +extension LightningActivity: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeLightningActivity: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LightningActivity { + return + try LightningActivity( + walletId: FfiConverterString.read(from: &buf), + id: FfiConverterString.read(from: &buf), + txType: FfiConverterTypePaymentType.read(from: &buf), + status: FfiConverterTypePaymentState.read(from: &buf), + value: FfiConverterUInt64.read(from: &buf), + fee: FfiConverterOptionUInt64.read(from: &buf), + invoice: FfiConverterString.read(from: &buf), + message: FfiConverterString.read(from: &buf), + timestamp: FfiConverterUInt64.read(from: &buf), + preimage: FfiConverterOptionString.read(from: &buf), + contact: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionUInt64.read(from: &buf), + updatedAt: FfiConverterOptionUInt64.read(from: &buf), + seenAt: FfiConverterOptionUInt64.read(from: &buf) + ) + } + + public static func write(_ value: LightningActivity, into buf: inout [UInt8]) { + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypePaymentType.write(value.txType, into: &buf) + FfiConverterTypePaymentState.write(value.status, into: &buf) + FfiConverterUInt64.write(value.value, into: &buf) + FfiConverterOptionUInt64.write(value.fee, into: &buf) + FfiConverterString.write(value.invoice, into: &buf) + FfiConverterString.write(value.message, into: &buf) + FfiConverterUInt64.write(value.timestamp, into: &buf) + FfiConverterOptionString.write(value.preimage, into: &buf) + FfiConverterOptionString.write(value.contact, into: &buf) + FfiConverterOptionUInt64.write(value.createdAt, into: &buf) + FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) + FfiConverterOptionUInt64.write(value.seenAt, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeLightningActivity_lift(_ buf: RustBuffer) throws -> LightningActivity { + return try FfiConverterTypeLightningActivity.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeLightningActivity_lower(_ value: LightningActivity) -> RustBuffer { + return FfiConverterTypeLightningActivity.lower(value) +} + + +public struct LightningInvoice { + public var bolt11: String + public var paymentHash: Data + public var amountSatoshis: UInt64 + public var timestampSeconds: UInt64 + public var expirySeconds: UInt64 + public var isExpired: Bool + public var description: String? + public var networkType: NetworkType + public var payeeNodeId: Data? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(bolt11: String, paymentHash: Data, amountSatoshis: UInt64, timestampSeconds: UInt64, expirySeconds: UInt64, isExpired: Bool, description: String?, networkType: NetworkType, payeeNodeId: Data?) { + self.bolt11 = bolt11 + self.paymentHash = paymentHash + self.amountSatoshis = amountSatoshis + self.timestampSeconds = timestampSeconds + self.expirySeconds = expirySeconds + self.isExpired = isExpired + self.description = description + self.networkType = networkType + self.payeeNodeId = payeeNodeId + } +} + +#if compiler(>=6) +extension LightningInvoice: Sendable {} +#endif + + +extension LightningInvoice: Equatable, Hashable { + public static func ==(lhs: LightningInvoice, rhs: LightningInvoice) -> Bool { + if lhs.bolt11 != rhs.bolt11 { return false } - if lhs.isManuallyPaid != rhs.isManuallyPaid { + if lhs.paymentHash != rhs.paymentHash { return false } - if lhs.btcAddress != rhs.btcAddress { + if lhs.amountSatoshis != rhs.amountSatoshis { return false } - if lhs.btcAddressId != rhs.btcAddressId { + if lhs.timestampSeconds != rhs.timestampSeconds { return false } - if lhs.bolt11Invoice != rhs.bolt11Invoice { + if lhs.expirySeconds != rhs.expirySeconds { return false } - if lhs.bolt11InvoiceId != rhs.bolt11InvoiceId { + if lhs.isExpired != rhs.isExpired { return false } - if lhs.manualRefunds != rhs.manualRefunds { + if lhs.description != rhs.description { + return false + } + if lhs.networkType != rhs.networkType { + return false + } + if lhs.payeeNodeId != rhs.payeeNodeId { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(state) - hasher.combine(oldState) - hasher.combine(onchainState) - hasher.combine(lnState) - hasher.combine(paidOnchainSat) - hasher.combine(paidLnSat) - hasher.combine(paidSat) - hasher.combine(isOverpaid) - hasher.combine(isRefunded) - hasher.combine(overpaidAmountSat) - hasher.combine(requiredOnchainConfirmations) - hasher.combine(settlementState) - hasher.combine(expectedAmountSat) - hasher.combine(isManuallyPaid) - hasher.combine(btcAddress) - hasher.combine(btcAddressId) - hasher.combine(bolt11Invoice) - hasher.combine(bolt11InvoiceId) - hasher.combine(manualRefunds) + hasher.combine(bolt11) + hasher.combine(paymentHash) + hasher.combine(amountSatoshis) + hasher.combine(timestampSeconds) + hasher.combine(expirySeconds) + hasher.combine(isExpired) + hasher.combine(description) + hasher.combine(networkType) + hasher.combine(payeeNodeId) } } -extension IGiftPayment: Codable {} +extension LightningInvoice: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIGiftPayment: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IGiftPayment { - return - try IGiftPayment( - id: FfiConverterString.read(from: &buf), - state: FfiConverterString.read(from: &buf), - oldState: FfiConverterOptionString.read(from: &buf), - onchainState: FfiConverterOptionString.read(from: &buf), - lnState: FfiConverterOptionString.read(from: &buf), - paidOnchainSat: FfiConverterOptionUInt64.read(from: &buf), - paidLnSat: FfiConverterOptionUInt64.read(from: &buf), - paidSat: FfiConverterOptionUInt64.read(from: &buf), - isOverpaid: FfiConverterOptionBool.read(from: &buf), - isRefunded: FfiConverterOptionBool.read(from: &buf), - overpaidAmountSat: FfiConverterOptionUInt64.read(from: &buf), - requiredOnchainConfirmations: FfiConverterOptionUInt32.read(from: &buf), - settlementState: FfiConverterOptionString.read(from: &buf), - expectedAmountSat: FfiConverterOptionUInt64.read(from: &buf), - isManuallyPaid: FfiConverterOptionBool.read(from: &buf), - btcAddress: FfiConverterOptionTypeIGiftBtcAddress.read(from: &buf), - btcAddressId: FfiConverterOptionString.read(from: &buf), - bolt11Invoice: FfiConverterOptionTypeIGiftBolt11Invoice.read(from: &buf), - bolt11InvoiceId: FfiConverterOptionString.read(from: &buf), - manualRefunds: FfiConverterSequenceString.read(from: &buf) - ) - } - - public static func write(_ value: IGiftPayment, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.state, into: &buf) - FfiConverterOptionString.write(value.oldState, into: &buf) - FfiConverterOptionString.write(value.onchainState, into: &buf) - FfiConverterOptionString.write(value.lnState, into: &buf) - FfiConverterOptionUInt64.write(value.paidOnchainSat, into: &buf) - FfiConverterOptionUInt64.write(value.paidLnSat, into: &buf) - FfiConverterOptionUInt64.write(value.paidSat, into: &buf) - FfiConverterOptionBool.write(value.isOverpaid, into: &buf) - FfiConverterOptionBool.write(value.isRefunded, into: &buf) - FfiConverterOptionUInt64.write(value.overpaidAmountSat, into: &buf) - FfiConverterOptionUInt32.write(value.requiredOnchainConfirmations, into: &buf) - FfiConverterOptionString.write(value.settlementState, into: &buf) - FfiConverterOptionUInt64.write(value.expectedAmountSat, into: &buf) - FfiConverterOptionBool.write(value.isManuallyPaid, into: &buf) - FfiConverterOptionTypeIGiftBtcAddress.write(value.btcAddress, into: &buf) - FfiConverterOptionString.write(value.btcAddressId, into: &buf) - FfiConverterOptionTypeIGiftBolt11Invoice.write(value.bolt11Invoice, into: &buf) - FfiConverterOptionString.write(value.bolt11InvoiceId, into: &buf) - FfiConverterSequenceString.write(value.manualRefunds, into: &buf) +public struct FfiConverterTypeLightningInvoice: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LightningInvoice { + return + try LightningInvoice( + bolt11: FfiConverterString.read(from: &buf), + paymentHash: FfiConverterData.read(from: &buf), + amountSatoshis: FfiConverterUInt64.read(from: &buf), + timestampSeconds: FfiConverterUInt64.read(from: &buf), + expirySeconds: FfiConverterUInt64.read(from: &buf), + isExpired: FfiConverterBool.read(from: &buf), + description: FfiConverterOptionString.read(from: &buf), + networkType: FfiConverterTypeNetworkType.read(from: &buf), + payeeNodeId: FfiConverterOptionData.read(from: &buf) + ) + } + + public static func write(_ value: LightningInvoice, into buf: inout [UInt8]) { + FfiConverterString.write(value.bolt11, into: &buf) + FfiConverterData.write(value.paymentHash, into: &buf) + FfiConverterUInt64.write(value.amountSatoshis, into: &buf) + FfiConverterUInt64.write(value.timestampSeconds, into: &buf) + FfiConverterUInt64.write(value.expirySeconds, into: &buf) + FfiConverterBool.write(value.isExpired, into: &buf) + FfiConverterOptionString.write(value.description, into: &buf) + FfiConverterTypeNetworkType.write(value.networkType, into: &buf) + FfiConverterOptionData.write(value.payeeNodeId, into: &buf) } } @@ -7899,87 +9967,79 @@ public struct FfiConverterTypeIGiftPayment: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftPayment_lift(_ buf: RustBuffer) throws -> IGiftPayment { - return try FfiConverterTypeIGiftPayment.lift(buf) +public func FfiConverterTypeLightningInvoice_lift(_ buf: RustBuffer) throws -> LightningInvoice { + return try FfiConverterTypeLightningInvoice.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIGiftPayment_lower(_ value: IGiftPayment) -> RustBuffer { - return FfiConverterTypeIGiftPayment.lower(value) +public func FfiConverterTypeLightningInvoice_lower(_ value: LightningInvoice) -> RustBuffer { + return FfiConverterTypeLightningInvoice.lower(value) } -public struct ILspNode { - public var alias: String - public var pubkey: String - public var connectionStrings: [String] - public var readonly: Bool? +public struct LnurlAddressData { + public var uri: String + public var domain: String + public var username: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(alias: String, pubkey: String, connectionStrings: [String], readonly: Bool?) { - self.alias = alias - self.pubkey = pubkey - self.connectionStrings = connectionStrings - self.readonly = readonly + public init(uri: String, domain: String, username: String) { + self.uri = uri + self.domain = domain + self.username = username } } #if compiler(>=6) -extension ILspNode: Sendable {} +extension LnurlAddressData: Sendable {} #endif -extension ILspNode: Equatable, Hashable { - public static func ==(lhs: ILspNode, rhs: ILspNode) -> Bool { - if lhs.alias != rhs.alias { - return false - } - if lhs.pubkey != rhs.pubkey { +extension LnurlAddressData: Equatable, Hashable { + public static func ==(lhs: LnurlAddressData, rhs: LnurlAddressData) -> Bool { + if lhs.uri != rhs.uri { return false } - if lhs.connectionStrings != rhs.connectionStrings { + if lhs.domain != rhs.domain { return false } - if lhs.readonly != rhs.readonly { + if lhs.username != rhs.username { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(alias) - hasher.combine(pubkey) - hasher.combine(connectionStrings) - hasher.combine(readonly) + hasher.combine(uri) + hasher.combine(domain) + hasher.combine(username) } } -extension ILspNode: Codable {} +extension LnurlAddressData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeILspNode: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ILspNode { +public struct FfiConverterTypeLnurlAddressData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlAddressData { return - try ILspNode( - alias: FfiConverterString.read(from: &buf), - pubkey: FfiConverterString.read(from: &buf), - connectionStrings: FfiConverterSequenceString.read(from: &buf), - readonly: FfiConverterOptionBool.read(from: &buf) + try LnurlAddressData( + uri: FfiConverterString.read(from: &buf), + domain: FfiConverterString.read(from: &buf), + username: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: ILspNode, into buf: inout [UInt8]) { - FfiConverterString.write(value.alias, into: &buf) - FfiConverterString.write(value.pubkey, into: &buf) - FfiConverterSequenceString.write(value.connectionStrings, into: &buf) - FfiConverterOptionBool.write(value.readonly, into: &buf) + public static func write(_ value: LnurlAddressData, into buf: inout [UInt8]) { + FfiConverterString.write(value.uri, into: &buf) + FfiConverterString.write(value.domain, into: &buf) + FfiConverterString.write(value.username, into: &buf) } } @@ -7987,111 +10047,87 @@ public struct FfiConverterTypeILspNode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeILspNode_lift(_ buf: RustBuffer) throws -> ILspNode { - return try FfiConverterTypeILspNode.lift(buf) +public func FfiConverterTypeLnurlAddressData_lift(_ buf: RustBuffer) throws -> LnurlAddressData { + return try FfiConverterTypeLnurlAddressData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeILspNode_lower(_ value: ILspNode) -> RustBuffer { - return FfiConverterTypeILspNode.lower(value) +public func FfiConverterTypeLnurlAddressData_lower(_ value: LnurlAddressData) -> RustBuffer { + return FfiConverterTypeLnurlAddressData.lower(value) } -public struct IManualRefund { - public var amountSat: UInt64 - public var target: String - public var state: ManualRefundStateEnum - public var createdByName: String - public var votedByName: String? - public var reason: String? - public var targetType: String +public struct LnurlAuthData { + public var uri: String + public var tag: String + public var k1: String + public var domain: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(amountSat: UInt64, target: String, state: ManualRefundStateEnum, createdByName: String, votedByName: String?, reason: String?, targetType: String) { - self.amountSat = amountSat - self.target = target - self.state = state - self.createdByName = createdByName - self.votedByName = votedByName - self.reason = reason - self.targetType = targetType + public init(uri: String, tag: String, k1: String, domain: String) { + self.uri = uri + self.tag = tag + self.k1 = k1 + self.domain = domain } } #if compiler(>=6) -extension IManualRefund: Sendable {} +extension LnurlAuthData: Sendable {} #endif -extension IManualRefund: Equatable, Hashable { - public static func ==(lhs: IManualRefund, rhs: IManualRefund) -> Bool { - if lhs.amountSat != rhs.amountSat { - return false - } - if lhs.target != rhs.target { - return false - } - if lhs.state != rhs.state { - return false - } - if lhs.createdByName != rhs.createdByName { +extension LnurlAuthData: Equatable, Hashable { + public static func ==(lhs: LnurlAuthData, rhs: LnurlAuthData) -> Bool { + if lhs.uri != rhs.uri { return false } - if lhs.votedByName != rhs.votedByName { + if lhs.tag != rhs.tag { return false } - if lhs.reason != rhs.reason { + if lhs.k1 != rhs.k1 { return false } - if lhs.targetType != rhs.targetType { + if lhs.domain != rhs.domain { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(amountSat) - hasher.combine(target) - hasher.combine(state) - hasher.combine(createdByName) - hasher.combine(votedByName) - hasher.combine(reason) - hasher.combine(targetType) + hasher.combine(uri) + hasher.combine(tag) + hasher.combine(k1) + hasher.combine(domain) } } -extension IManualRefund: Codable {} +extension LnurlAuthData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeIManualRefund: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IManualRefund { +public struct FfiConverterTypeLnurlAuthData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlAuthData { return - try IManualRefund( - amountSat: FfiConverterUInt64.read(from: &buf), - target: FfiConverterString.read(from: &buf), - state: FfiConverterTypeManualRefundStateEnum.read(from: &buf), - createdByName: FfiConverterString.read(from: &buf), - votedByName: FfiConverterOptionString.read(from: &buf), - reason: FfiConverterOptionString.read(from: &buf), - targetType: FfiConverterString.read(from: &buf) + try LnurlAuthData( + uri: FfiConverterString.read(from: &buf), + tag: FfiConverterString.read(from: &buf), + k1: FfiConverterString.read(from: &buf), + domain: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: IManualRefund, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.amountSat, into: &buf) - FfiConverterString.write(value.target, into: &buf) - FfiConverterTypeManualRefundStateEnum.write(value.state, into: &buf) - FfiConverterString.write(value.createdByName, into: &buf) - FfiConverterOptionString.write(value.votedByName, into: &buf) - FfiConverterOptionString.write(value.reason, into: &buf) - FfiConverterString.write(value.targetType, into: &buf) + public static func write(_ value: LnurlAuthData, into buf: inout [UInt8]) { + FfiConverterString.write(value.uri, into: &buf) + FfiConverterString.write(value.tag, into: &buf) + FfiConverterString.write(value.k1, into: &buf) + FfiConverterString.write(value.domain, into: &buf) } } @@ -8099,79 +10135,87 @@ public struct FfiConverterTypeIManualRefund: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIManualRefund_lift(_ buf: RustBuffer) throws -> IManualRefund { - return try FfiConverterTypeIManualRefund.lift(buf) +public func FfiConverterTypeLnurlAuthData_lift(_ buf: RustBuffer) throws -> LnurlAuthData { + return try FfiConverterTypeLnurlAuthData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeIManualRefund_lower(_ value: IManualRefund) -> RustBuffer { - return FfiConverterTypeIManualRefund.lower(value) +public func FfiConverterTypeLnurlAuthData_lower(_ value: LnurlAuthData) -> RustBuffer { + return FfiConverterTypeLnurlAuthData.lower(value) } -public struct JadeAccount { - public var variant: JadeAddressVariant - public var xpub: String - public var derivationPath: String +public struct LnurlChannelData { + public var uri: String + public var callback: String + public var k1: String + public var tag: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(variant: JadeAddressVariant, xpub: String, derivationPath: String) { - self.variant = variant - self.xpub = xpub - self.derivationPath = derivationPath + public init(uri: String, callback: String, k1: String, tag: String) { + self.uri = uri + self.callback = callback + self.k1 = k1 + self.tag = tag } } #if compiler(>=6) -extension JadeAccount: Sendable {} +extension LnurlChannelData: Sendable {} #endif -extension JadeAccount: Equatable, Hashable { - public static func ==(lhs: JadeAccount, rhs: JadeAccount) -> Bool { - if lhs.variant != rhs.variant { +extension LnurlChannelData: Equatable, Hashable { + public static func ==(lhs: LnurlChannelData, rhs: LnurlChannelData) -> Bool { + if lhs.uri != rhs.uri { return false } - if lhs.xpub != rhs.xpub { + if lhs.callback != rhs.callback { return false } - if lhs.derivationPath != rhs.derivationPath { + if lhs.k1 != rhs.k1 { + return false + } + if lhs.tag != rhs.tag { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(variant) - hasher.combine(xpub) - hasher.combine(derivationPath) + hasher.combine(uri) + hasher.combine(callback) + hasher.combine(k1) + hasher.combine(tag) } } -extension JadeAccount: Codable {} +extension LnurlChannelData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeAccount: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAccount { +public struct FfiConverterTypeLnurlChannelData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlChannelData { return - try JadeAccount( - variant: FfiConverterTypeJadeAddressVariant.read(from: &buf), - xpub: FfiConverterString.read(from: &buf), - derivationPath: FfiConverterString.read(from: &buf) + try LnurlChannelData( + uri: FfiConverterString.read(from: &buf), + callback: FfiConverterString.read(from: &buf), + k1: FfiConverterString.read(from: &buf), + tag: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: JadeAccount, into buf: inout [UInt8]) { - FfiConverterTypeJadeAddressVariant.write(value.variant, into: &buf) - FfiConverterString.write(value.xpub, into: &buf) - FfiConverterString.write(value.derivationPath, into: &buf) + public static func write(_ value: LnurlChannelData, into buf: inout [UInt8]) { + FfiConverterString.write(value.uri, into: &buf) + FfiConverterString.write(value.callback, into: &buf) + FfiConverterString.write(value.k1, into: &buf) + FfiConverterString.write(value.tag, into: &buf) } } @@ -8179,79 +10223,119 @@ public struct FfiConverterTypeJadeAccount: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAccount_lift(_ buf: RustBuffer) throws -> JadeAccount { - return try FfiConverterTypeJadeAccount.lift(buf) +public func FfiConverterTypeLnurlChannelData_lift(_ buf: RustBuffer) throws -> LnurlChannelData { + return try FfiConverterTypeLnurlChannelData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAccount_lower(_ value: JadeAccount) -> RustBuffer { - return FfiConverterTypeJadeAccount.lower(value) +public func FfiConverterTypeLnurlChannelData_lower(_ value: LnurlChannelData) -> RustBuffer { + return FfiConverterTypeLnurlChannelData.lower(value) } -public struct JadeAccountExport { - public var masterFingerprint: String - public var accountIndex: UInt32 - public var accounts: [JadeAccount] +public struct LnurlPayData { + public var uri: String + public var callback: String + public var minSendable: UInt64 + public var maxSendable: UInt64 + public var metadataStr: String + public var commentAllowed: UInt32? + public var allowsNostr: Bool + public var nostrPubkey: Data? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(masterFingerprint: String, accountIndex: UInt32, accounts: [JadeAccount]) { - self.masterFingerprint = masterFingerprint - self.accountIndex = accountIndex - self.accounts = accounts + public init(uri: String, callback: String, minSendable: UInt64, maxSendable: UInt64, metadataStr: String, commentAllowed: UInt32?, allowsNostr: Bool, nostrPubkey: Data?) { + self.uri = uri + self.callback = callback + self.minSendable = minSendable + self.maxSendable = maxSendable + self.metadataStr = metadataStr + self.commentAllowed = commentAllowed + self.allowsNostr = allowsNostr + self.nostrPubkey = nostrPubkey } } #if compiler(>=6) -extension JadeAccountExport: Sendable {} +extension LnurlPayData: Sendable {} #endif -extension JadeAccountExport: Equatable, Hashable { - public static func ==(lhs: JadeAccountExport, rhs: JadeAccountExport) -> Bool { - if lhs.masterFingerprint != rhs.masterFingerprint { +extension LnurlPayData: Equatable, Hashable { + public static func ==(lhs: LnurlPayData, rhs: LnurlPayData) -> Bool { + if lhs.uri != rhs.uri { return false } - if lhs.accountIndex != rhs.accountIndex { + if lhs.callback != rhs.callback { return false } - if lhs.accounts != rhs.accounts { + if lhs.minSendable != rhs.minSendable { + return false + } + if lhs.maxSendable != rhs.maxSendable { + return false + } + if lhs.metadataStr != rhs.metadataStr { + return false + } + if lhs.commentAllowed != rhs.commentAllowed { + return false + } + if lhs.allowsNostr != rhs.allowsNostr { + return false + } + if lhs.nostrPubkey != rhs.nostrPubkey { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(masterFingerprint) - hasher.combine(accountIndex) - hasher.combine(accounts) + hasher.combine(uri) + hasher.combine(callback) + hasher.combine(minSendable) + hasher.combine(maxSendable) + hasher.combine(metadataStr) + hasher.combine(commentAllowed) + hasher.combine(allowsNostr) + hasher.combine(nostrPubkey) } } -extension JadeAccountExport: Codable {} +extension LnurlPayData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeAccountExport: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAccountExport { +public struct FfiConverterTypeLnurlPayData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlPayData { return - try JadeAccountExport( - masterFingerprint: FfiConverterString.read(from: &buf), - accountIndex: FfiConverterUInt32.read(from: &buf), - accounts: FfiConverterSequenceTypeJadeAccount.read(from: &buf) + try LnurlPayData( + uri: FfiConverterString.read(from: &buf), + callback: FfiConverterString.read(from: &buf), + minSendable: FfiConverterUInt64.read(from: &buf), + maxSendable: FfiConverterUInt64.read(from: &buf), + metadataStr: FfiConverterString.read(from: &buf), + commentAllowed: FfiConverterOptionUInt32.read(from: &buf), + allowsNostr: FfiConverterBool.read(from: &buf), + nostrPubkey: FfiConverterOptionData.read(from: &buf) ) } - public static func write(_ value: JadeAccountExport, into buf: inout [UInt8]) { - FfiConverterString.write(value.masterFingerprint, into: &buf) - FfiConverterUInt32.write(value.accountIndex, into: &buf) - FfiConverterSequenceTypeJadeAccount.write(value.accounts, into: &buf) + public static func write(_ value: LnurlPayData, into buf: inout [UInt8]) { + FfiConverterString.write(value.uri, into: &buf) + FfiConverterString.write(value.callback, into: &buf) + FfiConverterUInt64.write(value.minSendable, into: &buf) + FfiConverterUInt64.write(value.maxSendable, into: &buf) + FfiConverterString.write(value.metadataStr, into: &buf) + FfiConverterOptionUInt32.write(value.commentAllowed, into: &buf) + FfiConverterBool.write(value.allowsNostr, into: &buf) + FfiConverterOptionData.write(value.nostrPubkey, into: &buf) } } @@ -8259,87 +10343,111 @@ public struct FfiConverterTypeJadeAccountExport: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAccountExport_lift(_ buf: RustBuffer) throws -> JadeAccountExport { - return try FfiConverterTypeJadeAccountExport.lift(buf) +public func FfiConverterTypeLnurlPayData_lift(_ buf: RustBuffer) throws -> LnurlPayData { + return try FfiConverterTypeLnurlPayData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAccountExport_lower(_ value: JadeAccountExport) -> RustBuffer { - return FfiConverterTypeJadeAccountExport.lower(value) +public func FfiConverterTypeLnurlPayData_lower(_ value: LnurlPayData) -> RustBuffer { + return FfiConverterTypeLnurlPayData.lower(value) } -public struct JadeDeviceInfo { - public var path: String - public var transport: JadeTransportKind - public var name: String? - public var serialNumber: String? +public struct LnurlWithdrawData { + public var uri: String + public var callback: String + public var k1: String + public var defaultDescription: String + public var minWithdrawable: UInt64? + public var maxWithdrawable: UInt64 + public var tag: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(path: String, transport: JadeTransportKind, name: String?, serialNumber: String?) { - self.path = path - self.transport = transport - self.name = name - self.serialNumber = serialNumber + public init(uri: String, callback: String, k1: String, defaultDescription: String, minWithdrawable: UInt64?, maxWithdrawable: UInt64, tag: String) { + self.uri = uri + self.callback = callback + self.k1 = k1 + self.defaultDescription = defaultDescription + self.minWithdrawable = minWithdrawable + self.maxWithdrawable = maxWithdrawable + self.tag = tag } } #if compiler(>=6) -extension JadeDeviceInfo: Sendable {} +extension LnurlWithdrawData: Sendable {} #endif -extension JadeDeviceInfo: Equatable, Hashable { - public static func ==(lhs: JadeDeviceInfo, rhs: JadeDeviceInfo) -> Bool { - if lhs.path != rhs.path { +extension LnurlWithdrawData: Equatable, Hashable { + public static func ==(lhs: LnurlWithdrawData, rhs: LnurlWithdrawData) -> Bool { + if lhs.uri != rhs.uri { return false } - if lhs.transport != rhs.transport { + if lhs.callback != rhs.callback { return false } - if lhs.name != rhs.name { + if lhs.k1 != rhs.k1 { return false } - if lhs.serialNumber != rhs.serialNumber { + if lhs.defaultDescription != rhs.defaultDescription { + return false + } + if lhs.minWithdrawable != rhs.minWithdrawable { + return false + } + if lhs.maxWithdrawable != rhs.maxWithdrawable { + return false + } + if lhs.tag != rhs.tag { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(transport) - hasher.combine(name) - hasher.combine(serialNumber) + hasher.combine(uri) + hasher.combine(callback) + hasher.combine(k1) + hasher.combine(defaultDescription) + hasher.combine(minWithdrawable) + hasher.combine(maxWithdrawable) + hasher.combine(tag) } } -extension JadeDeviceInfo: Codable {} +extension LnurlWithdrawData: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeDeviceInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeDeviceInfo { +public struct FfiConverterTypeLnurlWithdrawData: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlWithdrawData { return - try JadeDeviceInfo( - path: FfiConverterString.read(from: &buf), - transport: FfiConverterTypeJadeTransportKind.read(from: &buf), - name: FfiConverterOptionString.read(from: &buf), - serialNumber: FfiConverterOptionString.read(from: &buf) + try LnurlWithdrawData( + uri: FfiConverterString.read(from: &buf), + callback: FfiConverterString.read(from: &buf), + k1: FfiConverterString.read(from: &buf), + defaultDescription: FfiConverterString.read(from: &buf), + minWithdrawable: FfiConverterOptionUInt64.read(from: &buf), + maxWithdrawable: FfiConverterUInt64.read(from: &buf), + tag: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: JadeDeviceInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterTypeJadeTransportKind.write(value.transport, into: &buf) - FfiConverterOptionString.write(value.name, into: &buf) - FfiConverterOptionString.write(value.serialNumber, into: &buf) + public static func write(_ value: LnurlWithdrawData, into buf: inout [UInt8]) { + FfiConverterString.write(value.uri, into: &buf) + FfiConverterString.write(value.callback, into: &buf) + FfiConverterString.write(value.k1, into: &buf) + FfiConverterString.write(value.defaultDescription, into: &buf) + FfiConverterOptionUInt64.write(value.minWithdrawable, into: &buf) + FfiConverterUInt64.write(value.maxWithdrawable, into: &buf) + FfiConverterString.write(value.tag, into: &buf) } } @@ -8347,66 +10455,89 @@ public struct FfiConverterTypeJadeDeviceInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeDeviceInfo_lift(_ buf: RustBuffer) throws -> JadeDeviceInfo { - return try FfiConverterTypeJadeDeviceInfo.lift(buf) +public func FfiConverterTypeLnurlWithdrawData_lift(_ buf: RustBuffer) throws -> LnurlWithdrawData { + return try FfiConverterTypeLnurlWithdrawData.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeDeviceInfo_lower(_ value: JadeDeviceInfo) -> RustBuffer { - return FfiConverterTypeJadeDeviceInfo.lower(value) +public func FfiConverterTypeLnurlWithdrawData_lower(_ value: LnurlWithdrawData) -> RustBuffer { + return FfiConverterTypeLnurlWithdrawData.lower(value) } /** - * A device the native layer discovered. + * Native device information returned from enumeration */ -public struct JadeNativeDevice { +public struct NativeDeviceInfo { /** - * Transport specific address: a BLE identifier or a serial device path. + * Unique path/identifier for this device */ public var path: String - public var transport: JadeTransportKind /** - * Advertised or descriptor name, for example "Jade C0FFEE". + * Transport type: "usb" or "bluetooth" + */ + public var transportType: String + /** + * Optional device name (from BLE advertisement or USB descriptor) */ public var name: String? - public var serialNumber: String? + /** + * USB Vendor ID (for USB devices) + */ + public var vendorId: UInt16? + /** + * USB Product ID (for USB devices) + */ + public var productId: UInt16? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Transport specific address: a BLE identifier or a serial device path. - */path: String, transport: JadeTransportKind, + * Unique path/identifier for this device + */path: String, + /** + * Transport type: "usb" or "bluetooth" + */transportType: String, + /** + * Optional device name (from BLE advertisement or USB descriptor) + */name: String?, /** - * Advertised or descriptor name, for example "Jade C0FFEE". - */name: String?, serialNumber: String?) { + * USB Vendor ID (for USB devices) + */vendorId: UInt16?, + /** + * USB Product ID (for USB devices) + */productId: UInt16?) { self.path = path - self.transport = transport + self.transportType = transportType self.name = name - self.serialNumber = serialNumber + self.vendorId = vendorId + self.productId = productId } } #if compiler(>=6) -extension JadeNativeDevice: Sendable {} +extension NativeDeviceInfo: Sendable {} #endif -extension JadeNativeDevice: Equatable, Hashable { - public static func ==(lhs: JadeNativeDevice, rhs: JadeNativeDevice) -> Bool { +extension NativeDeviceInfo: Equatable, Hashable { + public static func ==(lhs: NativeDeviceInfo, rhs: NativeDeviceInfo) -> Bool { if lhs.path != rhs.path { return false } - if lhs.transport != rhs.transport { + if lhs.transportType != rhs.transportType { return false } if lhs.name != rhs.name { return false } - if lhs.serialNumber != rhs.serialNumber { + if lhs.vendorId != rhs.vendorId { + return false + } + if lhs.productId != rhs.productId { return false } return true @@ -8414,35 +10545,38 @@ extension JadeNativeDevice: Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(path) - hasher.combine(transport) + hasher.combine(transportType) hasher.combine(name) - hasher.combine(serialNumber) + hasher.combine(vendorId) + hasher.combine(productId) } } -extension JadeNativeDevice: Codable {} +extension NativeDeviceInfo: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeNativeDevice: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNativeDevice { +public struct FfiConverterTypeNativeDeviceInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativeDeviceInfo { return - try JadeNativeDevice( + try NativeDeviceInfo( path: FfiConverterString.read(from: &buf), - transport: FfiConverterTypeJadeTransportKind.read(from: &buf), + transportType: FfiConverterString.read(from: &buf), name: FfiConverterOptionString.read(from: &buf), - serialNumber: FfiConverterOptionString.read(from: &buf) + vendorId: FfiConverterOptionUInt16.read(from: &buf), + productId: FfiConverterOptionUInt16.read(from: &buf) ) } - public static func write(_ value: JadeNativeDevice, into buf: inout [UInt8]) { + public static func write(_ value: NativeDeviceInfo, into buf: inout [UInt8]) { FfiConverterString.write(value.path, into: &buf) - FfiConverterTypeJadeTransportKind.write(value.transport, into: &buf) + FfiConverterString.write(value.transportType, into: &buf) FfiConverterOptionString.write(value.name, into: &buf) - FfiConverterOptionString.write(value.serialNumber, into: &buf) + FfiConverterOptionUInt16.write(value.vendorId, into: &buf) + FfiConverterOptionUInt16.write(value.productId, into: &buf) } } @@ -8450,79 +10584,95 @@ public struct FfiConverterTypeJadeNativeDevice: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNativeDevice_lift(_ buf: RustBuffer) throws -> JadeNativeDevice { - return try FfiConverterTypeJadeNativeDevice.lift(buf) +public func FfiConverterTypeNativeDeviceInfo_lift(_ buf: RustBuffer) throws -> NativeDeviceInfo { + return try FfiConverterTypeNativeDeviceInfo.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNativeDevice_lower(_ value: JadeNativeDevice) -> RustBuffer { - return FfiConverterTypeJadeNativeDevice.lower(value) +public func FfiConverterTypeNativeDeviceInfo_lower(_ value: NativeDeviceInfo) -> RustBuffer { + return FfiConverterTypeNativeDeviceInfo.lower(value) } -public struct JadeSignedMessage { - public var signature: String +public struct OnChainInvoice { public var address: String - public var derivationPath: String + public var amountSatoshis: UInt64 + public var label: String? + public var message: String? + public var params: [String: String]? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(signature: String, address: String, derivationPath: String) { - self.signature = signature + public init(address: String, amountSatoshis: UInt64, label: String?, message: String?, params: [String: String]?) { self.address = address - self.derivationPath = derivationPath + self.amountSatoshis = amountSatoshis + self.label = label + self.message = message + self.params = params } } #if compiler(>=6) -extension JadeSignedMessage: Sendable {} +extension OnChainInvoice: Sendable {} #endif -extension JadeSignedMessage: Equatable, Hashable { - public static func ==(lhs: JadeSignedMessage, rhs: JadeSignedMessage) -> Bool { - if lhs.signature != rhs.signature { +extension OnChainInvoice: Equatable, Hashable { + public static func ==(lhs: OnChainInvoice, rhs: OnChainInvoice) -> Bool { + if lhs.address != rhs.address { return false } - if lhs.address != rhs.address { + if lhs.amountSatoshis != rhs.amountSatoshis { return false } - if lhs.derivationPath != rhs.derivationPath { + if lhs.label != rhs.label { + return false + } + if lhs.message != rhs.message { + return false + } + if lhs.params != rhs.params { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(signature) hasher.combine(address) - hasher.combine(derivationPath) + hasher.combine(amountSatoshis) + hasher.combine(label) + hasher.combine(message) + hasher.combine(params) } } -extension JadeSignedMessage: Codable {} +extension OnChainInvoice: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeSignedMessage: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeSignedMessage { +public struct FfiConverterTypeOnChainInvoice: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnChainInvoice { return - try JadeSignedMessage( - signature: FfiConverterString.read(from: &buf), + try OnChainInvoice( address: FfiConverterString.read(from: &buf), - derivationPath: FfiConverterString.read(from: &buf) + amountSatoshis: FfiConverterUInt64.read(from: &buf), + label: FfiConverterOptionString.read(from: &buf), + message: FfiConverterOptionString.read(from: &buf), + params: FfiConverterOptionDictionaryStringString.read(from: &buf) ) } - public static func write(_ value: JadeSignedMessage, into buf: inout [UInt8]) { - FfiConverterString.write(value.signature, into: &buf) + public static func write(_ value: OnChainInvoice, into buf: inout [UInt8]) { FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.derivationPath, into: &buf) + FfiConverterUInt64.write(value.amountSatoshis, into: &buf) + FfiConverterOptionString.write(value.label, into: &buf) + FfiConverterOptionString.write(value.message, into: &buf) + FfiConverterOptionDictionaryStringString.write(value.params, into: &buf) } } @@ -8530,104 +10680,223 @@ public struct FfiConverterTypeJadeSignedMessage: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeSignedMessage_lift(_ buf: RustBuffer) throws -> JadeSignedMessage { - return try FfiConverterTypeJadeSignedMessage.lift(buf) +public func FfiConverterTypeOnChainInvoice_lift(_ buf: RustBuffer) throws -> OnChainInvoice { + return try FfiConverterTypeOnChainInvoice.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeSignedMessage_lower(_ value: JadeSignedMessage) -> RustBuffer { - return FfiConverterTypeJadeSignedMessage.lower(value) +public func FfiConverterTypeOnChainInvoice_lower(_ value: OnChainInvoice) -> RustBuffer { + return FfiConverterTypeOnChainInvoice.lower(value) } -/** - * Outcome of a read. - */ -public struct JadeTransportReadResult { - public var success: Bool - /** - * Bytes read. Success with an empty vector means nothing has arrived yet, - * which is the normal case while the user is deciding on the device. - */ - public var data: Data - /** - * Empty on success. - */ - public var error: String - public var errorCode: JadeTransportErrorCode? +public struct OnchainActivity { + public var walletId: String + public var id: String + public var txType: PaymentType + public var txId: String + public var value: UInt64 + public var fee: UInt64 + public var feeRate: UInt64 + public var address: String + public var confirmed: Bool + public var timestamp: UInt64 + public var isBoosted: Bool + public var boostTxIds: [String] + public var isTransfer: Bool + public var doesExist: Bool + public var confirmTimestamp: UInt64? + public var channelId: String? + public var transferTxId: String? + public var contact: String? + public var createdAt: UInt64? + public var updatedAt: UInt64? + public var seenAt: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(success: Bool, - /** - * Bytes read. Success with an empty vector means nothing has arrived yet, - * which is the normal case while the user is deciding on the device. - */data: Data, - /** - * Empty on success. - */error: String, errorCode: JadeTransportErrorCode?) { - self.success = success - self.data = data - self.error = error - self.errorCode = errorCode + public init(walletId: String, id: String, txType: PaymentType, txId: String, value: UInt64, fee: UInt64, feeRate: UInt64, address: String, confirmed: Bool, timestamp: UInt64, isBoosted: Bool, boostTxIds: [String], isTransfer: Bool, doesExist: Bool, confirmTimestamp: UInt64?, channelId: String?, transferTxId: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { + self.walletId = walletId + self.id = id + self.txType = txType + self.txId = txId + self.value = value + self.fee = fee + self.feeRate = feeRate + self.address = address + self.confirmed = confirmed + self.timestamp = timestamp + self.isBoosted = isBoosted + self.boostTxIds = boostTxIds + self.isTransfer = isTransfer + self.doesExist = doesExist + self.confirmTimestamp = confirmTimestamp + self.channelId = channelId + self.transferTxId = transferTxId + self.contact = contact + self.createdAt = createdAt + self.updatedAt = updatedAt + self.seenAt = seenAt } } #if compiler(>=6) -extension JadeTransportReadResult: Sendable {} +extension OnchainActivity: Sendable {} #endif -extension JadeTransportReadResult: Equatable, Hashable { - public static func ==(lhs: JadeTransportReadResult, rhs: JadeTransportReadResult) -> Bool { - if lhs.success != rhs.success { +extension OnchainActivity: Equatable, Hashable { + public static func ==(lhs: OnchainActivity, rhs: OnchainActivity) -> Bool { + if lhs.walletId != rhs.walletId { + return false + } + if lhs.id != rhs.id { + return false + } + if lhs.txType != rhs.txType { + return false + } + if lhs.txId != rhs.txId { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.fee != rhs.fee { + return false + } + if lhs.feeRate != rhs.feeRate { + return false + } + if lhs.address != rhs.address { + return false + } + if lhs.confirmed != rhs.confirmed { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.isBoosted != rhs.isBoosted { + return false + } + if lhs.boostTxIds != rhs.boostTxIds { + return false + } + if lhs.isTransfer != rhs.isTransfer { + return false + } + if lhs.doesExist != rhs.doesExist { + return false + } + if lhs.confirmTimestamp != rhs.confirmTimestamp { + return false + } + if lhs.channelId != rhs.channelId { + return false + } + if lhs.transferTxId != rhs.transferTxId { + return false + } + if lhs.contact != rhs.contact { return false } - if lhs.data != rhs.data { + if lhs.createdAt != rhs.createdAt { return false } - if lhs.error != rhs.error { + if lhs.updatedAt != rhs.updatedAt { return false } - if lhs.errorCode != rhs.errorCode { + if lhs.seenAt != rhs.seenAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(success) - hasher.combine(data) - hasher.combine(error) - hasher.combine(errorCode) + hasher.combine(walletId) + hasher.combine(id) + hasher.combine(txType) + hasher.combine(txId) + hasher.combine(value) + hasher.combine(fee) + hasher.combine(feeRate) + hasher.combine(address) + hasher.combine(confirmed) + hasher.combine(timestamp) + hasher.combine(isBoosted) + hasher.combine(boostTxIds) + hasher.combine(isTransfer) + hasher.combine(doesExist) + hasher.combine(confirmTimestamp) + hasher.combine(channelId) + hasher.combine(transferTxId) + hasher.combine(contact) + hasher.combine(createdAt) + hasher.combine(updatedAt) + hasher.combine(seenAt) } } -extension JadeTransportReadResult: Codable {} +extension OnchainActivity: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportReadResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportReadResult { +public struct FfiConverterTypeOnchainActivity: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainActivity { return - try JadeTransportReadResult( - success: FfiConverterBool.read(from: &buf), - data: FfiConverterData.read(from: &buf), - error: FfiConverterString.read(from: &buf), - errorCode: FfiConverterOptionTypeJadeTransportErrorCode.read(from: &buf) + try OnchainActivity( + walletId: FfiConverterString.read(from: &buf), + id: FfiConverterString.read(from: &buf), + txType: FfiConverterTypePaymentType.read(from: &buf), + txId: FfiConverterString.read(from: &buf), + value: FfiConverterUInt64.read(from: &buf), + fee: FfiConverterUInt64.read(from: &buf), + feeRate: FfiConverterUInt64.read(from: &buf), + address: FfiConverterString.read(from: &buf), + confirmed: FfiConverterBool.read(from: &buf), + timestamp: FfiConverterUInt64.read(from: &buf), + isBoosted: FfiConverterBool.read(from: &buf), + boostTxIds: FfiConverterSequenceString.read(from: &buf), + isTransfer: FfiConverterBool.read(from: &buf), + doesExist: FfiConverterBool.read(from: &buf), + confirmTimestamp: FfiConverterOptionUInt64.read(from: &buf), + channelId: FfiConverterOptionString.read(from: &buf), + transferTxId: FfiConverterOptionString.read(from: &buf), + contact: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionUInt64.read(from: &buf), + updatedAt: FfiConverterOptionUInt64.read(from: &buf), + seenAt: FfiConverterOptionUInt64.read(from: &buf) ) } - public static func write(_ value: JadeTransportReadResult, into buf: inout [UInt8]) { - FfiConverterBool.write(value.success, into: &buf) - FfiConverterData.write(value.data, into: &buf) - FfiConverterString.write(value.error, into: &buf) - FfiConverterOptionTypeJadeTransportErrorCode.write(value.errorCode, into: &buf) + public static func write(_ value: OnchainActivity, into buf: inout [UInt8]) { + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypePaymentType.write(value.txType, into: &buf) + FfiConverterString.write(value.txId, into: &buf) + FfiConverterUInt64.write(value.value, into: &buf) + FfiConverterUInt64.write(value.fee, into: &buf) + FfiConverterUInt64.write(value.feeRate, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterBool.write(value.confirmed, into: &buf) + FfiConverterUInt64.write(value.timestamp, into: &buf) + FfiConverterBool.write(value.isBoosted, into: &buf) + FfiConverterSequenceString.write(value.boostTxIds, into: &buf) + FfiConverterBool.write(value.isTransfer, into: &buf) + FfiConverterBool.write(value.doesExist, into: &buf) + FfiConverterOptionUInt64.write(value.confirmTimestamp, into: &buf) + FfiConverterOptionString.write(value.channelId, into: &buf) + FfiConverterOptionString.write(value.transferTxId, into: &buf) + FfiConverterOptionString.write(value.contact, into: &buf) + FfiConverterOptionUInt64.write(value.createdAt, into: &buf) + FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) + FfiConverterOptionUInt64.write(value.seenAt, into: &buf) } } @@ -8635,88 +10904,94 @@ public struct FfiConverterTypeJadeTransportReadResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportReadResult_lift(_ buf: RustBuffer) throws -> JadeTransportReadResult { - return try FfiConverterTypeJadeTransportReadResult.lift(buf) +public func FfiConverterTypeOnchainActivity_lift(_ buf: RustBuffer) throws -> OnchainActivity { + return try FfiConverterTypeOnchainActivity.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportReadResult_lower(_ value: JadeTransportReadResult) -> RustBuffer { - return FfiConverterTypeJadeTransportReadResult.lower(value) +public func FfiConverterTypeOnchainActivity_lower(_ value: OnchainActivity) -> RustBuffer { + return FfiConverterTypeOnchainActivity.lower(value) } /** - * Outcome of an operation that returns no data. + * One single-signature account in Passport's generic JSON export. */ -public struct JadeTransportResult { - public var success: Bool +public struct PassportAccount { + public var accountType: AccountType /** - * Empty on success. + * Standard xpub/tpub encoding used by Passport's export. */ - public var error: String - public var errorCode: JadeTransportErrorCode? + public var xpub: String + /** + * Account-level BIP32 path, such as `m/84'/0'/0'`. + */ + public var derivationPath: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(success: Bool, + public init(accountType: AccountType, /** - * Empty on success. - */error: String, errorCode: JadeTransportErrorCode?) { - self.success = success - self.error = error - self.errorCode = errorCode + * Standard xpub/tpub encoding used by Passport's export. + */xpub: String, + /** + * Account-level BIP32 path, such as `m/84'/0'/0'`. + */derivationPath: String) { + self.accountType = accountType + self.xpub = xpub + self.derivationPath = derivationPath } } #if compiler(>=6) -extension JadeTransportResult: Sendable {} +extension PassportAccount: Sendable {} #endif -extension JadeTransportResult: Equatable, Hashable { - public static func ==(lhs: JadeTransportResult, rhs: JadeTransportResult) -> Bool { - if lhs.success != rhs.success { +extension PassportAccount: Equatable, Hashable { + public static func ==(lhs: PassportAccount, rhs: PassportAccount) -> Bool { + if lhs.accountType != rhs.accountType { return false } - if lhs.error != rhs.error { + if lhs.xpub != rhs.xpub { return false } - if lhs.errorCode != rhs.errorCode { + if lhs.derivationPath != rhs.derivationPath { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(success) - hasher.combine(error) - hasher.combine(errorCode) + hasher.combine(accountType) + hasher.combine(xpub) + hasher.combine(derivationPath) } } -extension JadeTransportResult: Codable {} +extension PassportAccount: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportResult { +public struct FfiConverterTypePassportAccount: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassportAccount { return - try JadeTransportResult( - success: FfiConverterBool.read(from: &buf), - error: FfiConverterString.read(from: &buf), - errorCode: FfiConverterOptionTypeJadeTransportErrorCode.read(from: &buf) + try PassportAccount( + accountType: FfiConverterTypeAccountType.read(from: &buf), + xpub: FfiConverterString.read(from: &buf), + derivationPath: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: JadeTransportResult, into buf: inout [UInt8]) { - FfiConverterBool.write(value.success, into: &buf) - FfiConverterString.write(value.error, into: &buf) - FfiConverterOptionTypeJadeTransportErrorCode.write(value.errorCode, into: &buf) + public static func write(_ value: PassportAccount, into buf: inout [UInt8]) { + FfiConverterTypeAccountType.write(value.accountType, into: &buf) + FfiConverterString.write(value.xpub, into: &buf) + FfiConverterString.write(value.derivationPath, into: &buf) } } @@ -8724,151 +10999,88 @@ public struct FfiConverterTypeJadeTransportResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportResult_lift(_ buf: RustBuffer) throws -> JadeTransportResult { - return try FfiConverterTypeJadeTransportResult.lift(buf) +public func FfiConverterTypePassportAccount_lift(_ buf: RustBuffer) throws -> PassportAccount { + return try FfiConverterTypePassportAccount.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportResult_lower(_ value: JadeTransportResult) -> RustBuffer { - return FfiConverterTypeJadeTransportResult.lower(value) -} - - -public struct JadeVersionInfo { - public var jadeVersion: String - public var jadeState: JadeState - public var jadeNetworks: String? - public var jadeHasPin: Bool? - public var boardType: String? - public var jadeConfig: String? - public var jadeFeatures: String? - public var idfVersion: String? - public var chipFeatures: String? - public var efuseMac: String? - public var batteryStatus: UInt32? - public var jadeOtaMaxChunk: UInt32? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(jadeVersion: String, jadeState: JadeState, jadeNetworks: String?, jadeHasPin: Bool?, boardType: String?, jadeConfig: String?, jadeFeatures: String?, idfVersion: String?, chipFeatures: String?, efuseMac: String?, batteryStatus: UInt32?, jadeOtaMaxChunk: UInt32?) { - self.jadeVersion = jadeVersion - self.jadeState = jadeState - self.jadeNetworks = jadeNetworks - self.jadeHasPin = jadeHasPin - self.boardType = boardType - self.jadeConfig = jadeConfig - self.jadeFeatures = jadeFeatures - self.idfVersion = idfVersion - self.chipFeatures = chipFeatures - self.efuseMac = efuseMac - self.batteryStatus = batteryStatus - self.jadeOtaMaxChunk = jadeOtaMaxChunk - } +public func FfiConverterTypePassportAccount_lower(_ value: PassportAccount) -> RustBuffer { + return FfiConverterTypePassportAccount.lower(value) } -#if compiler(>=6) -extension JadeVersionInfo: Sendable {} -#endif - -extension JadeVersionInfo: Equatable, Hashable { - public static func ==(lhs: JadeVersionInfo, rhs: JadeVersionInfo) -> Bool { - if lhs.jadeVersion != rhs.jadeVersion { - return false - } - if lhs.jadeState != rhs.jadeState { - return false - } - if lhs.jadeNetworks != rhs.jadeNetworks { - return false - } - if lhs.jadeHasPin != rhs.jadeHasPin { - return false - } - if lhs.boardType != rhs.boardType { - return false - } - if lhs.jadeConfig != rhs.jadeConfig { - return false - } - if lhs.jadeFeatures != rhs.jadeFeatures { - return false - } - if lhs.idfVersion != rhs.idfVersion { - return false - } - if lhs.chipFeatures != rhs.chipFeatures { - return false - } - if lhs.efuseMac != rhs.efuseMac { +/** + * The single-signature accounts exported by Passport for one account index. + */ +public struct PassportAccountExport { + /** + * Root fingerprint used in descriptors and PSBT key origins. + */ + public var masterFingerprint: String + public var accountIndex: UInt32 + public var accounts: [PassportAccount] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Root fingerprint used in descriptors and PSBT key origins. + */masterFingerprint: String, accountIndex: UInt32, accounts: [PassportAccount]) { + self.masterFingerprint = masterFingerprint + self.accountIndex = accountIndex + self.accounts = accounts + } +} + +#if compiler(>=6) +extension PassportAccountExport: Sendable {} +#endif + + +extension PassportAccountExport: Equatable, Hashable { + public static func ==(lhs: PassportAccountExport, rhs: PassportAccountExport) -> Bool { + if lhs.masterFingerprint != rhs.masterFingerprint { return false } - if lhs.batteryStatus != rhs.batteryStatus { + if lhs.accountIndex != rhs.accountIndex { return false } - if lhs.jadeOtaMaxChunk != rhs.jadeOtaMaxChunk { + if lhs.accounts != rhs.accounts { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(jadeVersion) - hasher.combine(jadeState) - hasher.combine(jadeNetworks) - hasher.combine(jadeHasPin) - hasher.combine(boardType) - hasher.combine(jadeConfig) - hasher.combine(jadeFeatures) - hasher.combine(idfVersion) - hasher.combine(chipFeatures) - hasher.combine(efuseMac) - hasher.combine(batteryStatus) - hasher.combine(jadeOtaMaxChunk) + hasher.combine(masterFingerprint) + hasher.combine(accountIndex) + hasher.combine(accounts) } } -extension JadeVersionInfo: Codable {} +extension PassportAccountExport: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeVersionInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeVersionInfo { +public struct FfiConverterTypePassportAccountExport: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassportAccountExport { return - try JadeVersionInfo( - jadeVersion: FfiConverterString.read(from: &buf), - jadeState: FfiConverterTypeJadeState.read(from: &buf), - jadeNetworks: FfiConverterOptionString.read(from: &buf), - jadeHasPin: FfiConverterOptionBool.read(from: &buf), - boardType: FfiConverterOptionString.read(from: &buf), - jadeConfig: FfiConverterOptionString.read(from: &buf), - jadeFeatures: FfiConverterOptionString.read(from: &buf), - idfVersion: FfiConverterOptionString.read(from: &buf), - chipFeatures: FfiConverterOptionString.read(from: &buf), - efuseMac: FfiConverterOptionString.read(from: &buf), - batteryStatus: FfiConverterOptionUInt32.read(from: &buf), - jadeOtaMaxChunk: FfiConverterOptionUInt32.read(from: &buf) + try PassportAccountExport( + masterFingerprint: FfiConverterString.read(from: &buf), + accountIndex: FfiConverterUInt32.read(from: &buf), + accounts: FfiConverterSequenceTypePassportAccount.read(from: &buf) ) } - public static func write(_ value: JadeVersionInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.jadeVersion, into: &buf) - FfiConverterTypeJadeState.write(value.jadeState, into: &buf) - FfiConverterOptionString.write(value.jadeNetworks, into: &buf) - FfiConverterOptionBool.write(value.jadeHasPin, into: &buf) - FfiConverterOptionString.write(value.boardType, into: &buf) - FfiConverterOptionString.write(value.jadeConfig, into: &buf) - FfiConverterOptionString.write(value.jadeFeatures, into: &buf) - FfiConverterOptionString.write(value.idfVersion, into: &buf) - FfiConverterOptionString.write(value.chipFeatures, into: &buf) - FfiConverterOptionString.write(value.efuseMac, into: &buf) - FfiConverterOptionUInt32.write(value.batteryStatus, into: &buf) - FfiConverterOptionUInt32.write(value.jadeOtaMaxChunk, into: &buf) + public static func write(_ value: PassportAccountExport, into buf: inout [UInt8]) { + FfiConverterString.write(value.masterFingerprint, into: &buf) + FfiConverterUInt32.write(value.accountIndex, into: &buf) + FfiConverterSequenceTypePassportAccount.write(value.accounts, into: &buf) } } @@ -8876,79 +11088,143 @@ public struct FfiConverterTypeJadeVersionInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeVersionInfo_lift(_ buf: RustBuffer) throws -> JadeVersionInfo { - return try FfiConverterTypeJadeVersionInfo.lift(buf) +public func FfiConverterTypePassportAccountExport_lift(_ buf: RustBuffer) throws -> PassportAccountExport { + return try FfiConverterTypePassportAccountExport.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeVersionInfo_lower(_ value: JadeVersionInfo) -> RustBuffer { - return FfiConverterTypeJadeVersionInfo.lower(value) +public func FfiConverterTypePassportAccountExport_lower(_ value: PassportAccountExport) -> RustBuffer { + return FfiConverterTypePassportAccountExport.lower(value) } -public struct JadeXpubResponse { - public var xpub: String - public var derivationPath: String - public var masterFingerprint: String +public struct PreActivityMetadata { + public var walletId: String + public var paymentId: String + public var tags: [String] + public var paymentHash: String? + public var txId: String? + public var address: String? + public var isReceive: Bool + public var feeRate: UInt64 + public var isTransfer: Bool + public var channelId: String? + public var createdAt: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(xpub: String, derivationPath: String, masterFingerprint: String) { - self.xpub = xpub - self.derivationPath = derivationPath - self.masterFingerprint = masterFingerprint + public init(walletId: String, paymentId: String, tags: [String], paymentHash: String?, txId: String?, address: String?, isReceive: Bool, feeRate: UInt64, isTransfer: Bool, channelId: String?, createdAt: UInt64) { + self.walletId = walletId + self.paymentId = paymentId + self.tags = tags + self.paymentHash = paymentHash + self.txId = txId + self.address = address + self.isReceive = isReceive + self.feeRate = feeRate + self.isTransfer = isTransfer + self.channelId = channelId + self.createdAt = createdAt } } #if compiler(>=6) -extension JadeXpubResponse: Sendable {} +extension PreActivityMetadata: Sendable {} #endif -extension JadeXpubResponse: Equatable, Hashable { - public static func ==(lhs: JadeXpubResponse, rhs: JadeXpubResponse) -> Bool { - if lhs.xpub != rhs.xpub { +extension PreActivityMetadata: Equatable, Hashable { + public static func ==(lhs: PreActivityMetadata, rhs: PreActivityMetadata) -> Bool { + if lhs.walletId != rhs.walletId { return false } - if lhs.derivationPath != rhs.derivationPath { + if lhs.paymentId != rhs.paymentId { return false } - if lhs.masterFingerprint != rhs.masterFingerprint { + if lhs.tags != rhs.tags { + return false + } + if lhs.paymentHash != rhs.paymentHash { + return false + } + if lhs.txId != rhs.txId { + return false + } + if lhs.address != rhs.address { + return false + } + if lhs.isReceive != rhs.isReceive { + return false + } + if lhs.feeRate != rhs.feeRate { + return false + } + if lhs.isTransfer != rhs.isTransfer { + return false + } + if lhs.channelId != rhs.channelId { + return false + } + if lhs.createdAt != rhs.createdAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(xpub) - hasher.combine(derivationPath) - hasher.combine(masterFingerprint) + hasher.combine(walletId) + hasher.combine(paymentId) + hasher.combine(tags) + hasher.combine(paymentHash) + hasher.combine(txId) + hasher.combine(address) + hasher.combine(isReceive) + hasher.combine(feeRate) + hasher.combine(isTransfer) + hasher.combine(channelId) + hasher.combine(createdAt) } } -extension JadeXpubResponse: Codable {} +extension PreActivityMetadata: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeXpubResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeXpubResponse { +public struct FfiConverterTypePreActivityMetadata: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PreActivityMetadata { return - try JadeXpubResponse( - xpub: FfiConverterString.read(from: &buf), - derivationPath: FfiConverterString.read(from: &buf), - masterFingerprint: FfiConverterString.read(from: &buf) + try PreActivityMetadata( + walletId: FfiConverterString.read(from: &buf), + paymentId: FfiConverterString.read(from: &buf), + tags: FfiConverterSequenceString.read(from: &buf), + paymentHash: FfiConverterOptionString.read(from: &buf), + txId: FfiConverterOptionString.read(from: &buf), + address: FfiConverterOptionString.read(from: &buf), + isReceive: FfiConverterBool.read(from: &buf), + feeRate: FfiConverterUInt64.read(from: &buf), + isTransfer: FfiConverterBool.read(from: &buf), + channelId: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: JadeXpubResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.xpub, into: &buf) - FfiConverterString.write(value.derivationPath, into: &buf) - FfiConverterString.write(value.masterFingerprint, into: &buf) + public static func write(_ value: PreActivityMetadata, into buf: inout [UInt8]) { + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.paymentId, into: &buf) + FfiConverterSequenceString.write(value.tags, into: &buf) + FfiConverterOptionString.write(value.paymentHash, into: &buf) + FfiConverterOptionString.write(value.txId, into: &buf) + FfiConverterOptionString.write(value.address, into: &buf) + FfiConverterBool.write(value.isReceive, into: &buf) + FfiConverterUInt64.write(value.feeRate, into: &buf) + FfiConverterBool.write(value.isTransfer, into: &buf) + FfiConverterOptionString.write(value.channelId, into: &buf) + FfiConverterUInt64.write(value.createdAt, into: &buf) } } @@ -8956,83 +11232,63 @@ public struct FfiConverterTypeJadeXpubResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeXpubResponse_lift(_ buf: RustBuffer) throws -> JadeXpubResponse { - return try FfiConverterTypeJadeXpubResponse.lift(buf) +public func FfiConverterTypePreActivityMetadata_lift(_ buf: RustBuffer) throws -> PreActivityMetadata { + return try FfiConverterTypePreActivityMetadata.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeXpubResponse_lower(_ value: JadeXpubResponse) -> RustBuffer { - return FfiConverterTypeJadeXpubResponse.lower(value) +public func FfiConverterTypePreActivityMetadata_lower(_ value: PreActivityMetadata) -> RustBuffer { + return FfiConverterTypePreActivityMetadata.lower(value) } -public struct LegacyRnCloseRecoveryScanResult { - /** - * Total balance found in legacy RN P2WPKH close outputs (in satoshis). - */ - public var totalAmount: UInt64 - /** - * Number of P2WPKH outputs found. - */ - public var outputsCount: UInt32 +public struct PubkyAuth { + public var data: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Total balance found in legacy RN P2WPKH close outputs (in satoshis). - */totalAmount: UInt64, - /** - * Number of P2WPKH outputs found. - */outputsCount: UInt32) { - self.totalAmount = totalAmount - self.outputsCount = outputsCount + public init(data: String) { + self.data = data } } #if compiler(>=6) -extension LegacyRnCloseRecoveryScanResult: Sendable {} +extension PubkyAuth: Sendable {} #endif -extension LegacyRnCloseRecoveryScanResult: Equatable, Hashable { - public static func ==(lhs: LegacyRnCloseRecoveryScanResult, rhs: LegacyRnCloseRecoveryScanResult) -> Bool { - if lhs.totalAmount != rhs.totalAmount { - return false - } - if lhs.outputsCount != rhs.outputsCount { +extension PubkyAuth: Equatable, Hashable { + public static func ==(lhs: PubkyAuth, rhs: PubkyAuth) -> Bool { + if lhs.data != rhs.data { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(totalAmount) - hasher.combine(outputsCount) + hasher.combine(data) } } -extension LegacyRnCloseRecoveryScanResult: Codable {} +extension PubkyAuth: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLegacyRnCloseRecoveryScanResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LegacyRnCloseRecoveryScanResult { +public struct FfiConverterTypePubkyAuth: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuth { return - try LegacyRnCloseRecoveryScanResult( - totalAmount: FfiConverterUInt64.read(from: &buf), - outputsCount: FfiConverterUInt32.read(from: &buf) + try PubkyAuth( + data: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: LegacyRnCloseRecoveryScanResult, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.totalAmount, into: &buf) - FfiConverterUInt32.write(value.outputsCount, into: &buf) + public static func write(_ value: PubkyAuth, into buf: inout [UInt8]) { + FfiConverterString.write(value.data, into: &buf) } } @@ -9040,167 +11296,128 @@ public struct FfiConverterTypeLegacyRnCloseRecoveryScanResult: FfiConverterRustB #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLegacyRnCloseRecoveryScanResult_lift(_ buf: RustBuffer) throws -> LegacyRnCloseRecoveryScanResult { - return try FfiConverterTypeLegacyRnCloseRecoveryScanResult.lift(buf) +public func FfiConverterTypePubkyAuth_lift(_ buf: RustBuffer) throws -> PubkyAuth { + return try FfiConverterTypePubkyAuth.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLegacyRnCloseRecoveryScanResult_lower(_ value: LegacyRnCloseRecoveryScanResult) -> RustBuffer { - return FfiConverterTypeLegacyRnCloseRecoveryScanResult.lower(value) +public func FfiConverterTypePubkyAuth_lower(_ value: PubkyAuth) -> RustBuffer { + return FfiConverterTypePubkyAuth.lower(value) } -public struct LegacyRnCloseRecoverySweepPreview { - /** - * Fully signed raw sweep transaction hex. Broadcast only after user confirmation. - */ - public var txHex: String - /** - * Transaction id of the sweep transaction. - */ - public var txid: String - /** - * Total input amount in satoshis. - */ - public var totalAmount: UInt64 +/** + * Details extracted from a `pubkyauth://` deep-link URL. + */ +public struct PubkyAuthDetails { /** - * Fee in satoshis. + * Whether this is a signin or signup flow. */ - public var estimatedFee: UInt64 + public var kind: PubkyAuthKind /** - * Transaction virtual size in vbytes. + * Requested capabilities (e.g. `"/pub/pubky.app/:rw"`). */ - public var estimatedVsize: UInt64 + public var capabilities: String /** - * Number of recovered outputs swept. + * Relay URL used for the auth exchange. */ - public var outputsCount: UInt32 + public var relay: String /** - * Destination address receiving the sweep. + * Homeserver public key (z32-encoded). Present only for signup flows. */ - public var destinationAddress: String + public var homeserver: String? /** - * Amount sent to destination after fees. + * Signup token. Present only for signup flows. */ - public var amountAfterFees: UInt64 + public var signupToken: String? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Fully signed raw sweep transaction hex. Broadcast only after user confirmation. - */txHex: String, - /** - * Transaction id of the sweep transaction. - */txid: String, - /** - * Total input amount in satoshis. - */totalAmount: UInt64, - /** - * Fee in satoshis. - */estimatedFee: UInt64, + * Whether this is a signin or signup flow. + */kind: PubkyAuthKind, /** - * Transaction virtual size in vbytes. - */estimatedVsize: UInt64, + * Requested capabilities (e.g. `"/pub/pubky.app/:rw"`). + */capabilities: String, /** - * Number of recovered outputs swept. - */outputsCount: UInt32, + * Relay URL used for the auth exchange. + */relay: String, /** - * Destination address receiving the sweep. - */destinationAddress: String, + * Homeserver public key (z32-encoded). Present only for signup flows. + */homeserver: String?, /** - * Amount sent to destination after fees. - */amountAfterFees: UInt64) { - self.txHex = txHex - self.txid = txid - self.totalAmount = totalAmount - self.estimatedFee = estimatedFee - self.estimatedVsize = estimatedVsize - self.outputsCount = outputsCount - self.destinationAddress = destinationAddress - self.amountAfterFees = amountAfterFees + * Signup token. Present only for signup flows. + */signupToken: String?) { + self.kind = kind + self.capabilities = capabilities + self.relay = relay + self.homeserver = homeserver + self.signupToken = signupToken } } #if compiler(>=6) -extension LegacyRnCloseRecoverySweepPreview: Sendable {} +extension PubkyAuthDetails: Sendable {} #endif -extension LegacyRnCloseRecoverySweepPreview: Equatable, Hashable { - public static func ==(lhs: LegacyRnCloseRecoverySweepPreview, rhs: LegacyRnCloseRecoverySweepPreview) -> Bool { - if lhs.txHex != rhs.txHex { - return false - } - if lhs.txid != rhs.txid { - return false - } - if lhs.totalAmount != rhs.totalAmount { - return false - } - if lhs.estimatedFee != rhs.estimatedFee { +extension PubkyAuthDetails: Equatable, Hashable { + public static func ==(lhs: PubkyAuthDetails, rhs: PubkyAuthDetails) -> Bool { + if lhs.kind != rhs.kind { return false } - if lhs.estimatedVsize != rhs.estimatedVsize { + if lhs.capabilities != rhs.capabilities { return false } - if lhs.outputsCount != rhs.outputsCount { + if lhs.relay != rhs.relay { return false } - if lhs.destinationAddress != rhs.destinationAddress { + if lhs.homeserver != rhs.homeserver { return false } - if lhs.amountAfterFees != rhs.amountAfterFees { + if lhs.signupToken != rhs.signupToken { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txHex) - hasher.combine(txid) - hasher.combine(totalAmount) - hasher.combine(estimatedFee) - hasher.combine(estimatedVsize) - hasher.combine(outputsCount) - hasher.combine(destinationAddress) - hasher.combine(amountAfterFees) + hasher.combine(kind) + hasher.combine(capabilities) + hasher.combine(relay) + hasher.combine(homeserver) + hasher.combine(signupToken) } } -extension LegacyRnCloseRecoverySweepPreview: Codable {} +extension PubkyAuthDetails: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLegacyRnCloseRecoverySweepPreview: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LegacyRnCloseRecoverySweepPreview { +public struct FfiConverterTypePubkyAuthDetails: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthDetails { return - try LegacyRnCloseRecoverySweepPreview( - txHex: FfiConverterString.read(from: &buf), - txid: FfiConverterString.read(from: &buf), - totalAmount: FfiConverterUInt64.read(from: &buf), - estimatedFee: FfiConverterUInt64.read(from: &buf), - estimatedVsize: FfiConverterUInt64.read(from: &buf), - outputsCount: FfiConverterUInt32.read(from: &buf), - destinationAddress: FfiConverterString.read(from: &buf), - amountAfterFees: FfiConverterUInt64.read(from: &buf) + try PubkyAuthDetails( + kind: FfiConverterTypePubkyAuthKind.read(from: &buf), + capabilities: FfiConverterString.read(from: &buf), + relay: FfiConverterString.read(from: &buf), + homeserver: FfiConverterOptionString.read(from: &buf), + signupToken: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: LegacyRnCloseRecoverySweepPreview, into buf: inout [UInt8]) { - FfiConverterString.write(value.txHex, into: &buf) - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt64.write(value.totalAmount, into: &buf) - FfiConverterUInt64.write(value.estimatedFee, into: &buf) - FfiConverterUInt64.write(value.estimatedVsize, into: &buf) - FfiConverterUInt32.write(value.outputsCount, into: &buf) - FfiConverterString.write(value.destinationAddress, into: &buf) - FfiConverterUInt64.write(value.amountAfterFees, into: &buf) + public static func write(_ value: PubkyAuthDetails, into buf: inout [UInt8]) { + FfiConverterTypePubkyAuthKind.write(value.kind, into: &buf) + FfiConverterString.write(value.capabilities, into: &buf) + FfiConverterString.write(value.relay, into: &buf) + FfiConverterOptionString.write(value.homeserver, into: &buf) + FfiConverterOptionString.write(value.signupToken, into: &buf) } } @@ -9208,167 +11425,95 @@ public struct FfiConverterTypeLegacyRnCloseRecoverySweepPreview: FfiConverterRus #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLegacyRnCloseRecoverySweepPreview_lift(_ buf: RustBuffer) throws -> LegacyRnCloseRecoverySweepPreview { - return try FfiConverterTypeLegacyRnCloseRecoverySweepPreview.lift(buf) +public func FfiConverterTypePubkyAuthDetails_lift(_ buf: RustBuffer) throws -> PubkyAuthDetails { + return try FfiConverterTypePubkyAuthDetails.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLegacyRnCloseRecoverySweepPreview_lower(_ value: LegacyRnCloseRecoverySweepPreview) -> RustBuffer { - return FfiConverterTypeLegacyRnCloseRecoverySweepPreview.lower(value) +public func FfiConverterTypePubkyAuthDetails_lower(_ value: PubkyAuthDetails) -> RustBuffer { + return FfiConverterTypePubkyAuthDetails.lower(value) } -public struct LightningActivity { - public var walletId: String - public var id: String - public var txType: PaymentType - public var status: PaymentState - public var value: UInt64 - public var fee: UInt64? - public var invoice: String - public var message: String - public var timestamp: UInt64 - public var preimage: String? - public var contact: String? - public var createdAt: UInt64? - public var updatedAt: UInt64? - public var seenAt: UInt64? +public struct PubkyProfile { + public var name: String + public var bio: String? + public var image: String? + public var links: [PubkyProfileLink]? + public var status: String? // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(walletId: String, id: String, txType: PaymentType, status: PaymentState, value: UInt64, fee: UInt64?, invoice: String, message: String, timestamp: UInt64, preimage: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { - self.walletId = walletId - self.id = id - self.txType = txType - self.status = status - self.value = value - self.fee = fee - self.invoice = invoice - self.message = message - self.timestamp = timestamp - self.preimage = preimage - self.contact = contact - self.createdAt = createdAt - self.updatedAt = updatedAt - self.seenAt = seenAt - } -} - -#if compiler(>=6) -extension LightningActivity: Sendable {} -#endif - - -extension LightningActivity: Equatable, Hashable { - public static func ==(lhs: LightningActivity, rhs: LightningActivity) -> Bool { - if lhs.walletId != rhs.walletId { - return false - } - if lhs.id != rhs.id { - return false - } - if lhs.txType != rhs.txType { - return false - } - if lhs.status != rhs.status { - return false - } - if lhs.value != rhs.value { - return false - } - if lhs.fee != rhs.fee { - return false - } - if lhs.invoice != rhs.invoice { - return false - } - if lhs.message != rhs.message { - return false - } - if lhs.timestamp != rhs.timestamp { - return false - } - if lhs.preimage != rhs.preimage { + // declare one manually. + public init(name: String, bio: String?, image: String?, links: [PubkyProfileLink]?, status: String?) { + self.name = name + self.bio = bio + self.image = image + self.links = links + self.status = status + } +} + +#if compiler(>=6) +extension PubkyProfile: Sendable {} +#endif + + +extension PubkyProfile: Equatable, Hashable { + public static func ==(lhs: PubkyProfile, rhs: PubkyProfile) -> Bool { + if lhs.name != rhs.name { return false } - if lhs.contact != rhs.contact { + if lhs.bio != rhs.bio { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.image != rhs.image { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.links != rhs.links { return false } - if lhs.seenAt != rhs.seenAt { + if lhs.status != rhs.status { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(walletId) - hasher.combine(id) - hasher.combine(txType) + hasher.combine(name) + hasher.combine(bio) + hasher.combine(image) + hasher.combine(links) hasher.combine(status) - hasher.combine(value) - hasher.combine(fee) - hasher.combine(invoice) - hasher.combine(message) - hasher.combine(timestamp) - hasher.combine(preimage) - hasher.combine(contact) - hasher.combine(createdAt) - hasher.combine(updatedAt) - hasher.combine(seenAt) } } -extension LightningActivity: Codable {} +extension PubkyProfile: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLightningActivity: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LightningActivity { +public struct FfiConverterTypePubkyProfile: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyProfile { return - try LightningActivity( - walletId: FfiConverterString.read(from: &buf), - id: FfiConverterString.read(from: &buf), - txType: FfiConverterTypePaymentType.read(from: &buf), - status: FfiConverterTypePaymentState.read(from: &buf), - value: FfiConverterUInt64.read(from: &buf), - fee: FfiConverterOptionUInt64.read(from: &buf), - invoice: FfiConverterString.read(from: &buf), - message: FfiConverterString.read(from: &buf), - timestamp: FfiConverterUInt64.read(from: &buf), - preimage: FfiConverterOptionString.read(from: &buf), - contact: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterOptionUInt64.read(from: &buf), - updatedAt: FfiConverterOptionUInt64.read(from: &buf), - seenAt: FfiConverterOptionUInt64.read(from: &buf) + try PubkyProfile( + name: FfiConverterString.read(from: &buf), + bio: FfiConverterOptionString.read(from: &buf), + image: FfiConverterOptionString.read(from: &buf), + links: FfiConverterOptionSequenceTypePubkyProfileLink.read(from: &buf), + status: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: LightningActivity, into buf: inout [UInt8]) { - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypePaymentType.write(value.txType, into: &buf) - FfiConverterTypePaymentState.write(value.status, into: &buf) - FfiConverterUInt64.write(value.value, into: &buf) - FfiConverterOptionUInt64.write(value.fee, into: &buf) - FfiConverterString.write(value.invoice, into: &buf) - FfiConverterString.write(value.message, into: &buf) - FfiConverterUInt64.write(value.timestamp, into: &buf) - FfiConverterOptionString.write(value.preimage, into: &buf) - FfiConverterOptionString.write(value.contact, into: &buf) - FfiConverterOptionUInt64.write(value.createdAt, into: &buf) - FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) - FfiConverterOptionUInt64.write(value.seenAt, into: &buf) + public static func write(_ value: PubkyProfile, into buf: inout [UInt8]) { + FfiConverterString.write(value.name, into: &buf) + FfiConverterOptionString.write(value.bio, into: &buf) + FfiConverterOptionString.write(value.image, into: &buf) + FfiConverterOptionSequenceTypePubkyProfileLink.write(value.links, into: &buf) + FfiConverterOptionString.write(value.status, into: &buf) } } @@ -9376,127 +11521,71 @@ public struct FfiConverterTypeLightningActivity: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLightningActivity_lift(_ buf: RustBuffer) throws -> LightningActivity { - return try FfiConverterTypeLightningActivity.lift(buf) +public func FfiConverterTypePubkyProfile_lift(_ buf: RustBuffer) throws -> PubkyProfile { + return try FfiConverterTypePubkyProfile.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLightningActivity_lower(_ value: LightningActivity) -> RustBuffer { - return FfiConverterTypeLightningActivity.lower(value) +public func FfiConverterTypePubkyProfile_lower(_ value: PubkyProfile) -> RustBuffer { + return FfiConverterTypePubkyProfile.lower(value) } -public struct LightningInvoice { - public var bolt11: String - public var paymentHash: Data - public var amountSatoshis: UInt64 - public var timestampSeconds: UInt64 - public var expirySeconds: UInt64 - public var isExpired: Bool - public var description: String? - public var networkType: NetworkType - public var payeeNodeId: Data? +public struct PubkyProfileLink { + public var title: String + public var url: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(bolt11: String, paymentHash: Data, amountSatoshis: UInt64, timestampSeconds: UInt64, expirySeconds: UInt64, isExpired: Bool, description: String?, networkType: NetworkType, payeeNodeId: Data?) { - self.bolt11 = bolt11 - self.paymentHash = paymentHash - self.amountSatoshis = amountSatoshis - self.timestampSeconds = timestampSeconds - self.expirySeconds = expirySeconds - self.isExpired = isExpired - self.description = description - self.networkType = networkType - self.payeeNodeId = payeeNodeId + public init(title: String, url: String) { + self.title = title + self.url = url } } #if compiler(>=6) -extension LightningInvoice: Sendable {} +extension PubkyProfileLink: Sendable {} #endif -extension LightningInvoice: Equatable, Hashable { - public static func ==(lhs: LightningInvoice, rhs: LightningInvoice) -> Bool { - if lhs.bolt11 != rhs.bolt11 { - return false - } - if lhs.paymentHash != rhs.paymentHash { - return false - } - if lhs.amountSatoshis != rhs.amountSatoshis { - return false - } - if lhs.timestampSeconds != rhs.timestampSeconds { - return false - } - if lhs.expirySeconds != rhs.expirySeconds { - return false - } - if lhs.isExpired != rhs.isExpired { - return false - } - if lhs.description != rhs.description { - return false - } - if lhs.networkType != rhs.networkType { +extension PubkyProfileLink: Equatable, Hashable { + public static func ==(lhs: PubkyProfileLink, rhs: PubkyProfileLink) -> Bool { + if lhs.title != rhs.title { return false } - if lhs.payeeNodeId != rhs.payeeNodeId { + if lhs.url != rhs.url { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(bolt11) - hasher.combine(paymentHash) - hasher.combine(amountSatoshis) - hasher.combine(timestampSeconds) - hasher.combine(expirySeconds) - hasher.combine(isExpired) - hasher.combine(description) - hasher.combine(networkType) - hasher.combine(payeeNodeId) + hasher.combine(title) + hasher.combine(url) } } -extension LightningInvoice: Codable {} +extension PubkyProfileLink: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLightningInvoice: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LightningInvoice { +public struct FfiConverterTypePubkyProfileLink: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyProfileLink { return - try LightningInvoice( - bolt11: FfiConverterString.read(from: &buf), - paymentHash: FfiConverterData.read(from: &buf), - amountSatoshis: FfiConverterUInt64.read(from: &buf), - timestampSeconds: FfiConverterUInt64.read(from: &buf), - expirySeconds: FfiConverterUInt64.read(from: &buf), - isExpired: FfiConverterBool.read(from: &buf), - description: FfiConverterOptionString.read(from: &buf), - networkType: FfiConverterTypeNetworkType.read(from: &buf), - payeeNodeId: FfiConverterOptionData.read(from: &buf) + try PubkyProfileLink( + title: FfiConverterString.read(from: &buf), + url: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: LightningInvoice, into buf: inout [UInt8]) { - FfiConverterString.write(value.bolt11, into: &buf) - FfiConverterData.write(value.paymentHash, into: &buf) - FfiConverterUInt64.write(value.amountSatoshis, into: &buf) - FfiConverterUInt64.write(value.timestampSeconds, into: &buf) - FfiConverterUInt64.write(value.expirySeconds, into: &buf) - FfiConverterBool.write(value.isExpired, into: &buf) - FfiConverterOptionString.write(value.description, into: &buf) - FfiConverterTypeNetworkType.write(value.networkType, into: &buf) - FfiConverterOptionData.write(value.payeeNodeId, into: &buf) + public static func write(_ value: PubkyProfileLink, into buf: inout [UInt8]) { + FfiConverterString.write(value.title, into: &buf) + FfiConverterString.write(value.url, into: &buf) } } @@ -9504,79 +11593,125 @@ public struct FfiConverterTypeLightningInvoice: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLightningInvoice_lift(_ buf: RustBuffer) throws -> LightningInvoice { - return try FfiConverterTypeLightningInvoice.lift(buf) +public func FfiConverterTypePubkyProfileLink_lift(_ buf: RustBuffer) throws -> PubkyProfileLink { + return try FfiConverterTypePubkyProfileLink.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLightningInvoice_lower(_ value: LightningInvoice) -> RustBuffer { - return FfiConverterTypeLightningInvoice.lower(value) +public func FfiConverterTypePubkyProfileLink_lower(_ value: PubkyProfileLink) -> RustBuffer { + return FfiConverterTypePubkyProfileLink.lower(value) } -public struct LnurlAddressData { - public var uri: String - public var domain: String - public var username: String +/** + * Result of creating a reverse swap (Lightning -> onchain). + * + * The caller pays `invoice` from its Lightning node; once Boltz locks funds at + * `lockup_address`, the module claims them to the provided onchain address. + */ +public struct ReverseSwapResponse { + public var id: String + /** + * Hold invoice the caller must pay via Lightning. + */ + public var invoice: String + /** + * Address Boltz locks the onchain funds to. + */ + public var lockupAddress: String + /** + * Amount in satoshis that will be received onchain (after Boltz fees). + */ + public var onchainAmountSat: UInt64 + /** + * Onchain timeout height for the swap. + */ + public var timeoutBlockHeight: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(uri: String, domain: String, username: String) { - self.uri = uri - self.domain = domain - self.username = username + public init(id: String, + /** + * Hold invoice the caller must pay via Lightning. + */invoice: String, + /** + * Address Boltz locks the onchain funds to. + */lockupAddress: String, + /** + * Amount in satoshis that will be received onchain (after Boltz fees). + */onchainAmountSat: UInt64, + /** + * Onchain timeout height for the swap. + */timeoutBlockHeight: UInt64) { + self.id = id + self.invoice = invoice + self.lockupAddress = lockupAddress + self.onchainAmountSat = onchainAmountSat + self.timeoutBlockHeight = timeoutBlockHeight } } #if compiler(>=6) -extension LnurlAddressData: Sendable {} +extension ReverseSwapResponse: Sendable {} #endif -extension LnurlAddressData: Equatable, Hashable { - public static func ==(lhs: LnurlAddressData, rhs: LnurlAddressData) -> Bool { - if lhs.uri != rhs.uri { +extension ReverseSwapResponse: Equatable, Hashable { + public static func ==(lhs: ReverseSwapResponse, rhs: ReverseSwapResponse) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.domain != rhs.domain { + if lhs.invoice != rhs.invoice { return false } - if lhs.username != rhs.username { + if lhs.lockupAddress != rhs.lockupAddress { + return false + } + if lhs.onchainAmountSat != rhs.onchainAmountSat { + return false + } + if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(uri) - hasher.combine(domain) - hasher.combine(username) + hasher.combine(id) + hasher.combine(invoice) + hasher.combine(lockupAddress) + hasher.combine(onchainAmountSat) + hasher.combine(timeoutBlockHeight) } } -extension LnurlAddressData: Codable {} +extension ReverseSwapResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlAddressData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlAddressData { +public struct FfiConverterTypeReverseSwapResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReverseSwapResponse { return - try LnurlAddressData( - uri: FfiConverterString.read(from: &buf), - domain: FfiConverterString.read(from: &buf), - username: FfiConverterString.read(from: &buf) + try ReverseSwapResponse( + id: FfiConverterString.read(from: &buf), + invoice: FfiConverterString.read(from: &buf), + lockupAddress: FfiConverterString.read(from: &buf), + onchainAmountSat: FfiConverterUInt64.read(from: &buf), + timeoutBlockHeight: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: LnurlAddressData, into buf: inout [UInt8]) { - FfiConverterString.write(value.uri, into: &buf) - FfiConverterString.write(value.domain, into: &buf) - FfiConverterString.write(value.username, into: &buf) + public static func write(_ value: ReverseSwapResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.invoice, into: &buf) + FfiConverterString.write(value.lockupAddress, into: &buf) + FfiConverterUInt64.write(value.onchainAmountSat, into: &buf) + FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) } } @@ -9584,87 +11719,128 @@ public struct FfiConverterTypeLnurlAddressData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlAddressData_lift(_ buf: RustBuffer) throws -> LnurlAddressData { - return try FfiConverterTypeLnurlAddressData.lift(buf) +public func FfiConverterTypeReverseSwapResponse_lift(_ buf: RustBuffer) throws -> ReverseSwapResponse { + return try FfiConverterTypeReverseSwapResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlAddressData_lower(_ value: LnurlAddressData) -> RustBuffer { - return FfiConverterTypeLnurlAddressData.lower(value) +public func FfiConverterTypeReverseSwapResponse_lower(_ value: ReverseSwapResponse) -> RustBuffer { + return FfiConverterTypeReverseSwapResponse.lower(value) } -public struct LnurlAuthData { - public var uri: String - public var tag: String - public var k1: String - public var domain: String +/** + * Result from querying a single Bitcoin address. + */ +public struct SingleAddressInfoResult { + /** + * The queried address + */ + public var address: String + /** + * Total confirmed balance in satoshis + */ + public var balance: UInt64 + /** + * UTXOs for this address + */ + public var utxos: [AccountUtxo] + /** + * Number of transactions involving this address + */ + public var transfers: UInt32 + /** + * Current blockchain tip height + */ + public var blockHeight: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(uri: String, tag: String, k1: String, domain: String) { - self.uri = uri - self.tag = tag - self.k1 = k1 - self.domain = domain + public init( + /** + * The queried address + */address: String, + /** + * Total confirmed balance in satoshis + */balance: UInt64, + /** + * UTXOs for this address + */utxos: [AccountUtxo], + /** + * Number of transactions involving this address + */transfers: UInt32, + /** + * Current blockchain tip height + */blockHeight: UInt32) { + self.address = address + self.balance = balance + self.utxos = utxos + self.transfers = transfers + self.blockHeight = blockHeight } } #if compiler(>=6) -extension LnurlAuthData: Sendable {} +extension SingleAddressInfoResult: Sendable {} #endif -extension LnurlAuthData: Equatable, Hashable { - public static func ==(lhs: LnurlAuthData, rhs: LnurlAuthData) -> Bool { - if lhs.uri != rhs.uri { +extension SingleAddressInfoResult: Equatable, Hashable { + public static func ==(lhs: SingleAddressInfoResult, rhs: SingleAddressInfoResult) -> Bool { + if lhs.address != rhs.address { return false } - if lhs.tag != rhs.tag { + if lhs.balance != rhs.balance { return false } - if lhs.k1 != rhs.k1 { + if lhs.utxos != rhs.utxos { return false } - if lhs.domain != rhs.domain { + if lhs.transfers != rhs.transfers { + return false + } + if lhs.blockHeight != rhs.blockHeight { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(uri) - hasher.combine(tag) - hasher.combine(k1) - hasher.combine(domain) + hasher.combine(address) + hasher.combine(balance) + hasher.combine(utxos) + hasher.combine(transfers) + hasher.combine(blockHeight) } } -extension LnurlAuthData: Codable {} +extension SingleAddressInfoResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlAuthData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlAuthData { +public struct FfiConverterTypeSingleAddressInfoResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SingleAddressInfoResult { return - try LnurlAuthData( - uri: FfiConverterString.read(from: &buf), - tag: FfiConverterString.read(from: &buf), - k1: FfiConverterString.read(from: &buf), - domain: FfiConverterString.read(from: &buf) + try SingleAddressInfoResult( + address: FfiConverterString.read(from: &buf), + balance: FfiConverterUInt64.read(from: &buf), + utxos: FfiConverterSequenceTypeAccountUtxo.read(from: &buf), + transfers: FfiConverterUInt32.read(from: &buf), + blockHeight: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: LnurlAuthData, into buf: inout [UInt8]) { - FfiConverterString.write(value.uri, into: &buf) - FfiConverterString.write(value.tag, into: &buf) - FfiConverterString.write(value.k1, into: &buf) - FfiConverterString.write(value.domain, into: &buf) + public static func write(_ value: SingleAddressInfoResult, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterUInt64.write(value.balance, into: &buf) + FfiConverterSequenceTypeAccountUtxo.write(value.utxos, into: &buf) + FfiConverterUInt32.write(value.transfers, into: &buf) + FfiConverterUInt32.write(value.blockHeight, into: &buf) } } @@ -9672,87 +11848,139 @@ public struct FfiConverterTypeLnurlAuthData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlAuthData_lift(_ buf: RustBuffer) throws -> LnurlAuthData { - return try FfiConverterTypeLnurlAuthData.lift(buf) +public func FfiConverterTypeSingleAddressInfoResult_lift(_ buf: RustBuffer) throws -> SingleAddressInfoResult { + return try FfiConverterTypeSingleAddressInfoResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlAuthData_lower(_ value: LnurlAuthData) -> RustBuffer { - return FfiConverterTypeLnurlAuthData.lower(value) +public func FfiConverterTypeSingleAddressInfoResult_lower(_ value: SingleAddressInfoResult) -> RustBuffer { + return FfiConverterTypeSingleAddressInfoResult.lower(value) } -public struct LnurlChannelData { - public var uri: String - public var callback: String - public var k1: String - public var tag: String +/** + * Result of creating a submarine swap (onchain -> Lightning). + * + * The caller funds `address` with `expected_amount_sat` from its onchain + * wallet; Boltz then pays the Lightning invoice supplied at creation. + */ +public struct SubmarineSwapResponse { + public var id: String + /** + * Onchain lockup address to send funds to. + */ + public var address: String + /** + * BIP21 URI for the lockup payment. + */ + public var bip21: String + /** + * Exact amount in satoshis the caller must send to `address`. + */ + public var expectedAmountSat: UInt64 + /** + * Whether Boltz will accept a zero-conf lockup. + */ + public var acceptZeroConf: Bool + /** + * Onchain timeout height after which a refund is possible. + */ + public var timeoutBlockHeight: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(uri: String, callback: String, k1: String, tag: String) { - self.uri = uri - self.callback = callback - self.k1 = k1 - self.tag = tag + public init(id: String, + /** + * Onchain lockup address to send funds to. + */address: String, + /** + * BIP21 URI for the lockup payment. + */bip21: String, + /** + * Exact amount in satoshis the caller must send to `address`. + */expectedAmountSat: UInt64, + /** + * Whether Boltz will accept a zero-conf lockup. + */acceptZeroConf: Bool, + /** + * Onchain timeout height after which a refund is possible. + */timeoutBlockHeight: UInt64) { + self.id = id + self.address = address + self.bip21 = bip21 + self.expectedAmountSat = expectedAmountSat + self.acceptZeroConf = acceptZeroConf + self.timeoutBlockHeight = timeoutBlockHeight } } #if compiler(>=6) -extension LnurlChannelData: Sendable {} +extension SubmarineSwapResponse: Sendable {} #endif -extension LnurlChannelData: Equatable, Hashable { - public static func ==(lhs: LnurlChannelData, rhs: LnurlChannelData) -> Bool { - if lhs.uri != rhs.uri { +extension SubmarineSwapResponse: Equatable, Hashable { + public static func ==(lhs: SubmarineSwapResponse, rhs: SubmarineSwapResponse) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.callback != rhs.callback { + if lhs.address != rhs.address { return false } - if lhs.k1 != rhs.k1 { + if lhs.bip21 != rhs.bip21 { + return false + } + if lhs.expectedAmountSat != rhs.expectedAmountSat { + return false + } + if lhs.acceptZeroConf != rhs.acceptZeroConf { return false } - if lhs.tag != rhs.tag { + if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(uri) - hasher.combine(callback) - hasher.combine(k1) - hasher.combine(tag) + hasher.combine(id) + hasher.combine(address) + hasher.combine(bip21) + hasher.combine(expectedAmountSat) + hasher.combine(acceptZeroConf) + hasher.combine(timeoutBlockHeight) } } -extension LnurlChannelData: Codable {} +extension SubmarineSwapResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlChannelData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlChannelData { +public struct FfiConverterTypeSubmarineSwapResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SubmarineSwapResponse { return - try LnurlChannelData( - uri: FfiConverterString.read(from: &buf), - callback: FfiConverterString.read(from: &buf), - k1: FfiConverterString.read(from: &buf), - tag: FfiConverterString.read(from: &buf) + try SubmarineSwapResponse( + id: FfiConverterString.read(from: &buf), + address: FfiConverterString.read(from: &buf), + bip21: FfiConverterString.read(from: &buf), + expectedAmountSat: FfiConverterUInt64.read(from: &buf), + acceptZeroConf: FfiConverterBool.read(from: &buf), + timeoutBlockHeight: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: LnurlChannelData, into buf: inout [UInt8]) { - FfiConverterString.write(value.uri, into: &buf) - FfiConverterString.write(value.callback, into: &buf) - FfiConverterString.write(value.k1, into: &buf) - FfiConverterString.write(value.tag, into: &buf) + public static func write(_ value: SubmarineSwapResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.bip21, into: &buf) + FfiConverterUInt64.write(value.expectedAmountSat, into: &buf) + FfiConverterBool.write(value.acceptZeroConf, into: &buf) + FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) } } @@ -9760,119 +11988,122 @@ public struct FfiConverterTypeLnurlChannelData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlChannelData_lift(_ buf: RustBuffer) throws -> LnurlChannelData { - return try FfiConverterTypeLnurlChannelData.lift(buf) +public func FfiConverterTypeSubmarineSwapResponse_lift(_ buf: RustBuffer) throws -> SubmarineSwapResponse { + return try FfiConverterTypeSubmarineSwapResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlChannelData_lower(_ value: LnurlChannelData) -> RustBuffer { - return FfiConverterTypeLnurlChannelData.lower(value) +public func FfiConverterTypeSubmarineSwapResponse_lower(_ value: SubmarineSwapResponse) -> RustBuffer { + return FfiConverterTypeSubmarineSwapResponse.lower(value) } -public struct LnurlPayData { - public var uri: String - public var callback: String - public var minSendable: UInt64 - public var maxSendable: UInt64 - public var metadataStr: String - public var commentAllowed: UInt32? - public var allowsNostr: Bool - public var nostrPubkey: Data? +/** + * A hardware-wallet model Bitkit supports. + */ +public struct SupportedHardwareWallet { + public var vendor: HardwareWalletVendor + /** + * Human-readable manufacturer name, e.g. "Foundation". + */ + public var vendorName: String + /** + * Stable model identifier that applications can map to bundled assets. + */ + public var model: String + /** + * Full user-facing name. + */ + public var displayName: String + /** + * Transports over which the application can interact with this model. + */ + public var transports: [HardwareWalletTransport] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(uri: String, callback: String, minSendable: UInt64, maxSendable: UInt64, metadataStr: String, commentAllowed: UInt32?, allowsNostr: Bool, nostrPubkey: Data?) { - self.uri = uri - self.callback = callback - self.minSendable = minSendable - self.maxSendable = maxSendable - self.metadataStr = metadataStr - self.commentAllowed = commentAllowed - self.allowsNostr = allowsNostr - self.nostrPubkey = nostrPubkey + public init(vendor: HardwareWalletVendor, + /** + * Human-readable manufacturer name, e.g. "Foundation". + */vendorName: String, + /** + * Stable model identifier that applications can map to bundled assets. + */model: String, + /** + * Full user-facing name. + */displayName: String, + /** + * Transports over which the application can interact with this model. + */transports: [HardwareWalletTransport]) { + self.vendor = vendor + self.vendorName = vendorName + self.model = model + self.displayName = displayName + self.transports = transports } } #if compiler(>=6) -extension LnurlPayData: Sendable {} +extension SupportedHardwareWallet: Sendable {} #endif -extension LnurlPayData: Equatable, Hashable { - public static func ==(lhs: LnurlPayData, rhs: LnurlPayData) -> Bool { - if lhs.uri != rhs.uri { - return false - } - if lhs.callback != rhs.callback { - return false - } - if lhs.minSendable != rhs.minSendable { - return false - } - if lhs.maxSendable != rhs.maxSendable { +extension SupportedHardwareWallet: Equatable, Hashable { + public static func ==(lhs: SupportedHardwareWallet, rhs: SupportedHardwareWallet) -> Bool { + if lhs.vendor != rhs.vendor { return false } - if lhs.metadataStr != rhs.metadataStr { + if lhs.vendorName != rhs.vendorName { return false } - if lhs.commentAllowed != rhs.commentAllowed { + if lhs.model != rhs.model { return false } - if lhs.allowsNostr != rhs.allowsNostr { + if lhs.displayName != rhs.displayName { return false } - if lhs.nostrPubkey != rhs.nostrPubkey { + if lhs.transports != rhs.transports { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(uri) - hasher.combine(callback) - hasher.combine(minSendable) - hasher.combine(maxSendable) - hasher.combine(metadataStr) - hasher.combine(commentAllowed) - hasher.combine(allowsNostr) - hasher.combine(nostrPubkey) + hasher.combine(vendor) + hasher.combine(vendorName) + hasher.combine(model) + hasher.combine(displayName) + hasher.combine(transports) } } -extension LnurlPayData: Codable {} +extension SupportedHardwareWallet: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlPayData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlPayData { +public struct FfiConverterTypeSupportedHardwareWallet: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SupportedHardwareWallet { return - try LnurlPayData( - uri: FfiConverterString.read(from: &buf), - callback: FfiConverterString.read(from: &buf), - minSendable: FfiConverterUInt64.read(from: &buf), - maxSendable: FfiConverterUInt64.read(from: &buf), - metadataStr: FfiConverterString.read(from: &buf), - commentAllowed: FfiConverterOptionUInt32.read(from: &buf), - allowsNostr: FfiConverterBool.read(from: &buf), - nostrPubkey: FfiConverterOptionData.read(from: &buf) + try SupportedHardwareWallet( + vendor: FfiConverterTypeHardwareWalletVendor.read(from: &buf), + vendorName: FfiConverterString.read(from: &buf), + model: FfiConverterString.read(from: &buf), + displayName: FfiConverterString.read(from: &buf), + transports: FfiConverterSequenceTypeHardwareWalletTransport.read(from: &buf) ) } - public static func write(_ value: LnurlPayData, into buf: inout [UInt8]) { - FfiConverterString.write(value.uri, into: &buf) - FfiConverterString.write(value.callback, into: &buf) - FfiConverterUInt64.write(value.minSendable, into: &buf) - FfiConverterUInt64.write(value.maxSendable, into: &buf) - FfiConverterString.write(value.metadataStr, into: &buf) - FfiConverterOptionUInt32.write(value.commentAllowed, into: &buf) - FfiConverterBool.write(value.allowsNostr, into: &buf) - FfiConverterOptionData.write(value.nostrPubkey, into: &buf) + public static func write(_ value: SupportedHardwareWallet, into buf: inout [UInt8]) { + FfiConverterTypeHardwareWalletVendor.write(value.vendor, into: &buf) + FfiConverterString.write(value.vendorName, into: &buf) + FfiConverterString.write(value.model, into: &buf) + FfiConverterString.write(value.displayName, into: &buf) + FfiConverterSequenceTypeHardwareWalletTransport.write(value.transports, into: &buf) } } @@ -9880,111 +12111,111 @@ public struct FfiConverterTypeLnurlPayData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlPayData_lift(_ buf: RustBuffer) throws -> LnurlPayData { - return try FfiConverterTypeLnurlPayData.lift(buf) +public func FfiConverterTypeSupportedHardwareWallet_lift(_ buf: RustBuffer) throws -> SupportedHardwareWallet { + return try FfiConverterTypeSupportedHardwareWallet.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlPayData_lower(_ value: LnurlPayData) -> RustBuffer { - return FfiConverterTypeLnurlPayData.lower(value) +public func FfiConverterTypeSupportedHardwareWallet_lower(_ value: SupportedHardwareWallet) -> RustBuffer { + return FfiConverterTypeSupportedHardwareWallet.lower(value) } -public struct LnurlWithdrawData { - public var uri: String - public var callback: String - public var k1: String - public var defaultDescription: String - public var minWithdrawable: UInt64? - public var maxWithdrawable: UInt64 - public var tag: String +public struct SweepResult { + /** + * The transaction ID of the sweep transaction + */ + public var txid: String + /** + * The total amount swept (in satoshis) + */ + public var amountSwept: UInt64 + /** + * The fee paid (in satoshis) + */ + public var feePaid: UInt64 + /** + * The number of UTXOs swept + */ + public var utxosSwept: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(uri: String, callback: String, k1: String, defaultDescription: String, minWithdrawable: UInt64?, maxWithdrawable: UInt64, tag: String) { - self.uri = uri - self.callback = callback - self.k1 = k1 - self.defaultDescription = defaultDescription - self.minWithdrawable = minWithdrawable - self.maxWithdrawable = maxWithdrawable - self.tag = tag + public init( + /** + * The transaction ID of the sweep transaction + */txid: String, + /** + * The total amount swept (in satoshis) + */amountSwept: UInt64, + /** + * The fee paid (in satoshis) + */feePaid: UInt64, + /** + * The number of UTXOs swept + */utxosSwept: UInt32) { + self.txid = txid + self.amountSwept = amountSwept + self.feePaid = feePaid + self.utxosSwept = utxosSwept } } #if compiler(>=6) -extension LnurlWithdrawData: Sendable {} +extension SweepResult: Sendable {} #endif -extension LnurlWithdrawData: Equatable, Hashable { - public static func ==(lhs: LnurlWithdrawData, rhs: LnurlWithdrawData) -> Bool { - if lhs.uri != rhs.uri { - return false - } - if lhs.callback != rhs.callback { - return false - } - if lhs.k1 != rhs.k1 { - return false - } - if lhs.defaultDescription != rhs.defaultDescription { +extension SweepResult: Equatable, Hashable { + public static func ==(lhs: SweepResult, rhs: SweepResult) -> Bool { + if lhs.txid != rhs.txid { return false } - if lhs.minWithdrawable != rhs.minWithdrawable { + if lhs.amountSwept != rhs.amountSwept { return false } - if lhs.maxWithdrawable != rhs.maxWithdrawable { + if lhs.feePaid != rhs.feePaid { return false } - if lhs.tag != rhs.tag { + if lhs.utxosSwept != rhs.utxosSwept { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(uri) - hasher.combine(callback) - hasher.combine(k1) - hasher.combine(defaultDescription) - hasher.combine(minWithdrawable) - hasher.combine(maxWithdrawable) - hasher.combine(tag) + hasher.combine(txid) + hasher.combine(amountSwept) + hasher.combine(feePaid) + hasher.combine(utxosSwept) } } -extension LnurlWithdrawData: Codable {} +extension SweepResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlWithdrawData: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlWithdrawData { +public struct FfiConverterTypeSweepResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepResult { return - try LnurlWithdrawData( - uri: FfiConverterString.read(from: &buf), - callback: FfiConverterString.read(from: &buf), - k1: FfiConverterString.read(from: &buf), - defaultDescription: FfiConverterString.read(from: &buf), - minWithdrawable: FfiConverterOptionUInt64.read(from: &buf), - maxWithdrawable: FfiConverterUInt64.read(from: &buf), - tag: FfiConverterString.read(from: &buf) + try SweepResult( + txid: FfiConverterString.read(from: &buf), + amountSwept: FfiConverterUInt64.read(from: &buf), + feePaid: FfiConverterUInt64.read(from: &buf), + utxosSwept: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: LnurlWithdrawData, into buf: inout [UInt8]) { - FfiConverterString.write(value.uri, into: &buf) - FfiConverterString.write(value.callback, into: &buf) - FfiConverterString.write(value.k1, into: &buf) - FfiConverterString.write(value.defaultDescription, into: &buf) - FfiConverterOptionUInt64.write(value.minWithdrawable, into: &buf) - FfiConverterUInt64.write(value.maxWithdrawable, into: &buf) - FfiConverterString.write(value.tag, into: &buf) + public static func write(_ value: SweepResult, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt64.write(value.amountSwept, into: &buf) + FfiConverterUInt64.write(value.feePaid, into: &buf) + FfiConverterUInt32.write(value.utxosSwept, into: &buf) } } @@ -9992,128 +12223,153 @@ public struct FfiConverterTypeLnurlWithdrawData: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlWithdrawData_lift(_ buf: RustBuffer) throws -> LnurlWithdrawData { - return try FfiConverterTypeLnurlWithdrawData.lift(buf) +public func FfiConverterTypeSweepResult_lift(_ buf: RustBuffer) throws -> SweepResult { + return try FfiConverterTypeSweepResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlWithdrawData_lower(_ value: LnurlWithdrawData) -> RustBuffer { - return FfiConverterTypeLnurlWithdrawData.lower(value) +public func FfiConverterTypeSweepResult_lower(_ value: SweepResult) -> RustBuffer { + return FfiConverterTypeSweepResult.lower(value) } -/** - * Native device information returned from enumeration - */ -public struct NativeDeviceInfo { +public struct SweepTransactionPreview { /** - * Unique path/identifier for this device + * The PSBT (Partially Signed Bitcoin Transaction) in base64 format */ - public var path: String + public var psbt: String /** - * Transport type: "usb" or "bluetooth" + * The total amount available to sweep (in satoshis) */ - public var transportType: String + public var totalAmount: UInt64 /** - * Optional device name (from BLE advertisement or USB descriptor) + * The estimated fee for the transaction (in satoshis) */ - public var name: String? + public var estimatedFee: UInt64 /** - * USB Vendor ID (for USB devices) + * The estimated virtual size of the transaction (in vbytes) */ - public var vendorId: UInt16? + public var estimatedVsize: UInt64 /** - * USB Product ID (for USB devices) + * The number of UTXOs that will be swept */ - public var productId: UInt16? + public var utxosCount: UInt32 + /** + * The destination address + */ + public var destinationAddress: String + /** + * The amount that will be sent to destination after fees (in satoshis) + */ + public var amountAfterFees: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Unique path/identifier for this device - */path: String, + * The PSBT (Partially Signed Bitcoin Transaction) in base64 format + */psbt: String, /** - * Transport type: "usb" or "bluetooth" - */transportType: String, + * The total amount available to sweep (in satoshis) + */totalAmount: UInt64, /** - * Optional device name (from BLE advertisement or USB descriptor) - */name: String?, + * The estimated fee for the transaction (in satoshis) + */estimatedFee: UInt64, /** - * USB Vendor ID (for USB devices) - */vendorId: UInt16?, + * The estimated virtual size of the transaction (in vbytes) + */estimatedVsize: UInt64, /** - * USB Product ID (for USB devices) - */productId: UInt16?) { - self.path = path - self.transportType = transportType - self.name = name - self.vendorId = vendorId - self.productId = productId + * The number of UTXOs that will be swept + */utxosCount: UInt32, + /** + * The destination address + */destinationAddress: String, + /** + * The amount that will be sent to destination after fees (in satoshis) + */amountAfterFees: UInt64) { + self.psbt = psbt + self.totalAmount = totalAmount + self.estimatedFee = estimatedFee + self.estimatedVsize = estimatedVsize + self.utxosCount = utxosCount + self.destinationAddress = destinationAddress + self.amountAfterFees = amountAfterFees } } #if compiler(>=6) -extension NativeDeviceInfo: Sendable {} +extension SweepTransactionPreview: Sendable {} #endif -extension NativeDeviceInfo: Equatable, Hashable { - public static func ==(lhs: NativeDeviceInfo, rhs: NativeDeviceInfo) -> Bool { - if lhs.path != rhs.path { +extension SweepTransactionPreview: Equatable, Hashable { + public static func ==(lhs: SweepTransactionPreview, rhs: SweepTransactionPreview) -> Bool { + if lhs.psbt != rhs.psbt { return false } - if lhs.transportType != rhs.transportType { + if lhs.totalAmount != rhs.totalAmount { return false } - if lhs.name != rhs.name { + if lhs.estimatedFee != rhs.estimatedFee { return false } - if lhs.vendorId != rhs.vendorId { + if lhs.estimatedVsize != rhs.estimatedVsize { return false } - if lhs.productId != rhs.productId { + if lhs.utxosCount != rhs.utxosCount { + return false + } + if lhs.destinationAddress != rhs.destinationAddress { + return false + } + if lhs.amountAfterFees != rhs.amountAfterFees { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(transportType) - hasher.combine(name) - hasher.combine(vendorId) - hasher.combine(productId) + hasher.combine(psbt) + hasher.combine(totalAmount) + hasher.combine(estimatedFee) + hasher.combine(estimatedVsize) + hasher.combine(utxosCount) + hasher.combine(destinationAddress) + hasher.combine(amountAfterFees) } } -extension NativeDeviceInfo: Codable {} +extension SweepTransactionPreview: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeNativeDeviceInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NativeDeviceInfo { +public struct FfiConverterTypeSweepTransactionPreview: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepTransactionPreview { return - try NativeDeviceInfo( - path: FfiConverterString.read(from: &buf), - transportType: FfiConverterString.read(from: &buf), - name: FfiConverterOptionString.read(from: &buf), - vendorId: FfiConverterOptionUInt16.read(from: &buf), - productId: FfiConverterOptionUInt16.read(from: &buf) + try SweepTransactionPreview( + psbt: FfiConverterString.read(from: &buf), + totalAmount: FfiConverterUInt64.read(from: &buf), + estimatedFee: FfiConverterUInt64.read(from: &buf), + estimatedVsize: FfiConverterUInt64.read(from: &buf), + utxosCount: FfiConverterUInt32.read(from: &buf), + destinationAddress: FfiConverterString.read(from: &buf), + amountAfterFees: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: NativeDeviceInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterString.write(value.transportType, into: &buf) - FfiConverterOptionString.write(value.name, into: &buf) - FfiConverterOptionUInt16.write(value.vendorId, into: &buf) - FfiConverterOptionUInt16.write(value.productId, into: &buf) + public static func write(_ value: SweepTransactionPreview, into buf: inout [UInt8]) { + FfiConverterString.write(value.psbt, into: &buf) + FfiConverterUInt64.write(value.totalAmount, into: &buf) + FfiConverterUInt64.write(value.estimatedFee, into: &buf) + FfiConverterUInt64.write(value.estimatedVsize, into: &buf) + FfiConverterUInt32.write(value.utxosCount, into: &buf) + FfiConverterString.write(value.destinationAddress, into: &buf) + FfiConverterUInt64.write(value.amountAfterFees, into: &buf) } } @@ -10121,95 +12377,167 @@ public struct FfiConverterTypeNativeDeviceInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNativeDeviceInfo_lift(_ buf: RustBuffer) throws -> NativeDeviceInfo { - return try FfiConverterTypeNativeDeviceInfo.lift(buf) +public func FfiConverterTypeSweepTransactionPreview_lift(_ buf: RustBuffer) throws -> SweepTransactionPreview { + return try FfiConverterTypeSweepTransactionPreview.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNativeDeviceInfo_lower(_ value: NativeDeviceInfo) -> RustBuffer { - return FfiConverterTypeNativeDeviceInfo.lower(value) +public func FfiConverterTypeSweepTransactionPreview_lower(_ value: SweepTransactionPreview) -> RustBuffer { + return FfiConverterTypeSweepTransactionPreview.lower(value) } -public struct OnChainInvoice { - public var address: String - public var amountSatoshis: UInt64 - public var label: String? - public var message: String? - public var params: [String: String]? +public struct SweepableBalances { + /** + * Balance in legacy (P2PKH) addresses (in satoshis) + */ + public var legacyBalance: UInt64 + /** + * Balance in P2SH-SegWit (P2SH-P2WPKH) addresses (in satoshis) + */ + public var p2shBalance: UInt64 + /** + * Balance in Taproot (P2TR) addresses (in satoshis) + */ + public var taprootBalance: UInt64 + /** + * Total balance across all wallet types (in satoshis) + */ + public var totalBalance: UInt64 + /** + * Number of UTXOs in legacy wallet + */ + public var legacyUtxosCount: UInt32 + /** + * Number of UTXOs in P2SH-SegWit wallet + */ + public var p2shUtxosCount: UInt32 + /** + * Number of UTXOs in Taproot wallet + */ + public var taprootUtxosCount: UInt32 + /** + * Total number of UTXOs across all wallet types + */ + public var totalUtxosCount: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(address: String, amountSatoshis: UInt64, label: String?, message: String?, params: [String: String]?) { - self.address = address - self.amountSatoshis = amountSatoshis - self.label = label - self.message = message - self.params = params + public init( + /** + * Balance in legacy (P2PKH) addresses (in satoshis) + */legacyBalance: UInt64, + /** + * Balance in P2SH-SegWit (P2SH-P2WPKH) addresses (in satoshis) + */p2shBalance: UInt64, + /** + * Balance in Taproot (P2TR) addresses (in satoshis) + */taprootBalance: UInt64, + /** + * Total balance across all wallet types (in satoshis) + */totalBalance: UInt64, + /** + * Number of UTXOs in legacy wallet + */legacyUtxosCount: UInt32, + /** + * Number of UTXOs in P2SH-SegWit wallet + */p2shUtxosCount: UInt32, + /** + * Number of UTXOs in Taproot wallet + */taprootUtxosCount: UInt32, + /** + * Total number of UTXOs across all wallet types + */totalUtxosCount: UInt32) { + self.legacyBalance = legacyBalance + self.p2shBalance = p2shBalance + self.taprootBalance = taprootBalance + self.totalBalance = totalBalance + self.legacyUtxosCount = legacyUtxosCount + self.p2shUtxosCount = p2shUtxosCount + self.taprootUtxosCount = taprootUtxosCount + self.totalUtxosCount = totalUtxosCount } } #if compiler(>=6) -extension OnChainInvoice: Sendable {} +extension SweepableBalances: Sendable {} #endif -extension OnChainInvoice: Equatable, Hashable { - public static func ==(lhs: OnChainInvoice, rhs: OnChainInvoice) -> Bool { - if lhs.address != rhs.address { +extension SweepableBalances: Equatable, Hashable { + public static func ==(lhs: SweepableBalances, rhs: SweepableBalances) -> Bool { + if lhs.legacyBalance != rhs.legacyBalance { return false } - if lhs.amountSatoshis != rhs.amountSatoshis { + if lhs.p2shBalance != rhs.p2shBalance { return false } - if lhs.label != rhs.label { + if lhs.taprootBalance != rhs.taprootBalance { return false } - if lhs.message != rhs.message { + if lhs.totalBalance != rhs.totalBalance { return false } - if lhs.params != rhs.params { + if lhs.legacyUtxosCount != rhs.legacyUtxosCount { + return false + } + if lhs.p2shUtxosCount != rhs.p2shUtxosCount { + return false + } + if lhs.taprootUtxosCount != rhs.taprootUtxosCount { + return false + } + if lhs.totalUtxosCount != rhs.totalUtxosCount { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(amountSatoshis) - hasher.combine(label) - hasher.combine(message) - hasher.combine(params) + hasher.combine(legacyBalance) + hasher.combine(p2shBalance) + hasher.combine(taprootBalance) + hasher.combine(totalBalance) + hasher.combine(legacyUtxosCount) + hasher.combine(p2shUtxosCount) + hasher.combine(taprootUtxosCount) + hasher.combine(totalUtxosCount) } } -extension OnChainInvoice: Codable {} +extension SweepableBalances: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeOnChainInvoice: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnChainInvoice { +public struct FfiConverterTypeSweepableBalances: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepableBalances { return - try OnChainInvoice( - address: FfiConverterString.read(from: &buf), - amountSatoshis: FfiConverterUInt64.read(from: &buf), - label: FfiConverterOptionString.read(from: &buf), - message: FfiConverterOptionString.read(from: &buf), - params: FfiConverterOptionDictionaryStringString.read(from: &buf) + try SweepableBalances( + legacyBalance: FfiConverterUInt64.read(from: &buf), + p2shBalance: FfiConverterUInt64.read(from: &buf), + taprootBalance: FfiConverterUInt64.read(from: &buf), + totalBalance: FfiConverterUInt64.read(from: &buf), + legacyUtxosCount: FfiConverterUInt32.read(from: &buf), + p2shUtxosCount: FfiConverterUInt32.read(from: &buf), + taprootUtxosCount: FfiConverterUInt32.read(from: &buf), + totalUtxosCount: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: OnChainInvoice, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterUInt64.write(value.amountSatoshis, into: &buf) - FfiConverterOptionString.write(value.label, into: &buf) - FfiConverterOptionString.write(value.message, into: &buf) - FfiConverterOptionDictionaryStringString.write(value.params, into: &buf) + public static func write(_ value: SweepableBalances, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.legacyBalance, into: &buf) + FfiConverterUInt64.write(value.p2shBalance, into: &buf) + FfiConverterUInt64.write(value.taprootBalance, into: &buf) + FfiConverterUInt64.write(value.totalBalance, into: &buf) + FfiConverterUInt32.write(value.legacyUtxosCount, into: &buf) + FfiConverterUInt32.write(value.p2shUtxosCount, into: &buf) + FfiConverterUInt32.write(value.taprootUtxosCount, into: &buf) + FfiConverterUInt32.write(value.totalUtxosCount, into: &buf) } } @@ -10217,318 +12545,282 @@ public struct FfiConverterTypeOnChainInvoice: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnChainInvoice_lift(_ buf: RustBuffer) throws -> OnChainInvoice { - return try FfiConverterTypeOnChainInvoice.lift(buf) +public func FfiConverterTypeSweepableBalances_lift(_ buf: RustBuffer) throws -> SweepableBalances { + return try FfiConverterTypeSweepableBalances.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnChainInvoice_lower(_ value: OnChainInvoice) -> RustBuffer { - return FfiConverterTypeOnChainInvoice.lower(value) +public func FfiConverterTypeSweepableBalances_lower(_ value: SweepableBalances) -> RustBuffer { + return FfiConverterTypeSweepableBalances.lower(value) } -public struct OnchainActivity { - public var walletId: String - public var id: String - public var txType: PaymentType - public var txId: String - public var value: UInt64 - public var fee: UInt64 - public var feeRate: UInt64 - public var address: String - public var confirmed: Bool - public var timestamp: UInt64 - public var isBoosted: Bool - public var boostTxIds: [String] - public var isTransfer: Bool - public var doesExist: Bool - public var confirmTimestamp: UInt64? - public var channelId: String? - public var transferTxId: String? - public var contact: String? - public var createdAt: UInt64? - public var updatedAt: UInt64? - public var seenAt: UInt64? +/** + * Full details for a single transaction, including raw inputs/outputs and size metrics. + */ +public struct TransactionDetail { + /** + * Transaction ID (hex) + */ + public var txid: String + /** + * Amount received by the wallet (sats) + */ + public var received: UInt64 + /** + * Amount sent by the wallet (sats) — includes change sent back to self + */ + public var sent: UInt64 + /** + * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) + */ + public var net: Int64 + /** + * Display amount in sats (same semantics as HistoryTransaction.amount) + */ + public var amount: UInt64 + /** + * Transaction fee in sats (None if not available) + */ + public var fee: UInt64? + /** + * Transaction direction + */ + public var direction: TxDirection + /** + * Block height (None if unconfirmed/mempool) + */ + public var blockHeight: UInt32? + /** + * Block timestamp as unix epoch seconds (None if unconfirmed) + */ + public var timestamp: UInt64? + /** + * Number of confirmations (0 if unconfirmed) + */ + public var confirmations: UInt32 + /** + * Transaction inputs + */ + public var inputs: [TxDetailInput] + /** + * Transaction outputs + */ + public var outputs: [TxDetailOutput] + /** + * Serialized transaction size in bytes + */ + public var size: UInt32 + /** + * Virtual size in vbytes (ceil(weight / 4)) + */ + public var vsize: UInt32 + /** + * Transaction weight in weight units + */ + public var weight: UInt32 + /** + * Fee rate in sat/vB (fee / vsize), None if fee is unavailable or vsize is zero + */ + public var feeRate: Double? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(walletId: String, id: String, txType: PaymentType, txId: String, value: UInt64, fee: UInt64, feeRate: UInt64, address: String, confirmed: Bool, timestamp: UInt64, isBoosted: Bool, boostTxIds: [String], isTransfer: Bool, doesExist: Bool, confirmTimestamp: UInt64?, channelId: String?, transferTxId: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { - self.walletId = walletId - self.id = id - self.txType = txType - self.txId = txId - self.value = value + public init( + /** + * Transaction ID (hex) + */txid: String, + /** + * Amount received by the wallet (sats) + */received: UInt64, + /** + * Amount sent by the wallet (sats) — includes change sent back to self + */sent: UInt64, + /** + * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) + */net: Int64, + /** + * Display amount in sats (same semantics as HistoryTransaction.amount) + */amount: UInt64, + /** + * Transaction fee in sats (None if not available) + */fee: UInt64?, + /** + * Transaction direction + */direction: TxDirection, + /** + * Block height (None if unconfirmed/mempool) + */blockHeight: UInt32?, + /** + * Block timestamp as unix epoch seconds (None if unconfirmed) + */timestamp: UInt64?, + /** + * Number of confirmations (0 if unconfirmed) + */confirmations: UInt32, + /** + * Transaction inputs + */inputs: [TxDetailInput], + /** + * Transaction outputs + */outputs: [TxDetailOutput], + /** + * Serialized transaction size in bytes + */size: UInt32, + /** + * Virtual size in vbytes (ceil(weight / 4)) + */vsize: UInt32, + /** + * Transaction weight in weight units + */weight: UInt32, + /** + * Fee rate in sat/vB (fee / vsize), None if fee is unavailable or vsize is zero + */feeRate: Double?) { + self.txid = txid + self.received = received + self.sent = sent + self.net = net + self.amount = amount self.fee = fee - self.feeRate = feeRate - self.address = address - self.confirmed = confirmed + self.direction = direction + self.blockHeight = blockHeight self.timestamp = timestamp - self.isBoosted = isBoosted - self.boostTxIds = boostTxIds - self.isTransfer = isTransfer - self.doesExist = doesExist - self.confirmTimestamp = confirmTimestamp - self.channelId = channelId - self.transferTxId = transferTxId - self.contact = contact - self.createdAt = createdAt - self.updatedAt = updatedAt - self.seenAt = seenAt + self.confirmations = confirmations + self.inputs = inputs + self.outputs = outputs + self.size = size + self.vsize = vsize + self.weight = weight + self.feeRate = feeRate } } #if compiler(>=6) -extension OnchainActivity: Sendable {} +extension TransactionDetail: Sendable {} #endif -extension OnchainActivity: Equatable, Hashable { - public static func ==(lhs: OnchainActivity, rhs: OnchainActivity) -> Bool { - if lhs.walletId != rhs.walletId { +extension TransactionDetail: Equatable, Hashable { + public static func ==(lhs: TransactionDetail, rhs: TransactionDetail) -> Bool { + if lhs.txid != rhs.txid { return false } - if lhs.id != rhs.id { + if lhs.received != rhs.received { return false } - if lhs.txType != rhs.txType { + if lhs.sent != rhs.sent { return false } - if lhs.txId != rhs.txId { + if lhs.net != rhs.net { return false } - if lhs.value != rhs.value { + if lhs.amount != rhs.amount { return false } if lhs.fee != rhs.fee { return false } - if lhs.feeRate != rhs.feeRate { - return false - } - if lhs.address != rhs.address { + if lhs.direction != rhs.direction { return false } - if lhs.confirmed != rhs.confirmed { + if lhs.blockHeight != rhs.blockHeight { return false } if lhs.timestamp != rhs.timestamp { return false } - if lhs.isBoosted != rhs.isBoosted { - return false - } - if lhs.boostTxIds != rhs.boostTxIds { - return false - } - if lhs.isTransfer != rhs.isTransfer { - return false - } - if lhs.doesExist != rhs.doesExist { - return false - } - if lhs.confirmTimestamp != rhs.confirmTimestamp { + if lhs.confirmations != rhs.confirmations { return false } - if lhs.channelId != rhs.channelId { + if lhs.inputs != rhs.inputs { return false } - if lhs.transferTxId != rhs.transferTxId { + if lhs.outputs != rhs.outputs { return false } - if lhs.contact != rhs.contact { + if lhs.size != rhs.size { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.vsize != rhs.vsize { return false } - if lhs.updatedAt != rhs.updatedAt { + if lhs.weight != rhs.weight { return false } - if lhs.seenAt != rhs.seenAt { + if lhs.feeRate != rhs.feeRate { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(walletId) - hasher.combine(id) - hasher.combine(txType) - hasher.combine(txId) - hasher.combine(value) + hasher.combine(txid) + hasher.combine(received) + hasher.combine(sent) + hasher.combine(net) + hasher.combine(amount) hasher.combine(fee) - hasher.combine(feeRate) - hasher.combine(address) - hasher.combine(confirmed) + hasher.combine(direction) + hasher.combine(blockHeight) hasher.combine(timestamp) - hasher.combine(isBoosted) - hasher.combine(boostTxIds) - hasher.combine(isTransfer) - hasher.combine(doesExist) - hasher.combine(confirmTimestamp) - hasher.combine(channelId) - hasher.combine(transferTxId) - hasher.combine(contact) - hasher.combine(createdAt) - hasher.combine(updatedAt) - hasher.combine(seenAt) - } -} - -extension OnchainActivity: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeOnchainActivity: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainActivity { - return - try OnchainActivity( - walletId: FfiConverterString.read(from: &buf), - id: FfiConverterString.read(from: &buf), - txType: FfiConverterTypePaymentType.read(from: &buf), - txId: FfiConverterString.read(from: &buf), - value: FfiConverterUInt64.read(from: &buf), - fee: FfiConverterUInt64.read(from: &buf), - feeRate: FfiConverterUInt64.read(from: &buf), - address: FfiConverterString.read(from: &buf), - confirmed: FfiConverterBool.read(from: &buf), - timestamp: FfiConverterUInt64.read(from: &buf), - isBoosted: FfiConverterBool.read(from: &buf), - boostTxIds: FfiConverterSequenceString.read(from: &buf), - isTransfer: FfiConverterBool.read(from: &buf), - doesExist: FfiConverterBool.read(from: &buf), - confirmTimestamp: FfiConverterOptionUInt64.read(from: &buf), - channelId: FfiConverterOptionString.read(from: &buf), - transferTxId: FfiConverterOptionString.read(from: &buf), - contact: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterOptionUInt64.read(from: &buf), - updatedAt: FfiConverterOptionUInt64.read(from: &buf), - seenAt: FfiConverterOptionUInt64.read(from: &buf) - ) - } - - public static func write(_ value: OnchainActivity, into buf: inout [UInt8]) { - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypePaymentType.write(value.txType, into: &buf) - FfiConverterString.write(value.txId, into: &buf) - FfiConverterUInt64.write(value.value, into: &buf) - FfiConverterUInt64.write(value.fee, into: &buf) - FfiConverterUInt64.write(value.feeRate, into: &buf) - FfiConverterString.write(value.address, into: &buf) - FfiConverterBool.write(value.confirmed, into: &buf) - FfiConverterUInt64.write(value.timestamp, into: &buf) - FfiConverterBool.write(value.isBoosted, into: &buf) - FfiConverterSequenceString.write(value.boostTxIds, into: &buf) - FfiConverterBool.write(value.isTransfer, into: &buf) - FfiConverterBool.write(value.doesExist, into: &buf) - FfiConverterOptionUInt64.write(value.confirmTimestamp, into: &buf) - FfiConverterOptionString.write(value.channelId, into: &buf) - FfiConverterOptionString.write(value.transferTxId, into: &buf) - FfiConverterOptionString.write(value.contact, into: &buf) - FfiConverterOptionUInt64.write(value.createdAt, into: &buf) - FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) - FfiConverterOptionUInt64.write(value.seenAt, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeOnchainActivity_lift(_ buf: RustBuffer) throws -> OnchainActivity { - return try FfiConverterTypeOnchainActivity.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeOnchainActivity_lower(_ value: OnchainActivity) -> RustBuffer { - return FfiConverterTypeOnchainActivity.lower(value) -} - - -/** - * One single-signature account in Passport's generic JSON export. - */ -public struct PassportAccount { - public var accountType: AccountType - /** - * Standard xpub/tpub encoding used by Passport's export. - */ - public var xpub: String - /** - * Account-level BIP32 path, such as `m/84'/0'/0'`. - */ - public var derivationPath: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(accountType: AccountType, - /** - * Standard xpub/tpub encoding used by Passport's export. - */xpub: String, - /** - * Account-level BIP32 path, such as `m/84'/0'/0'`. - */derivationPath: String) { - self.accountType = accountType - self.xpub = xpub - self.derivationPath = derivationPath - } -} - -#if compiler(>=6) -extension PassportAccount: Sendable {} -#endif - - -extension PassportAccount: Equatable, Hashable { - public static func ==(lhs: PassportAccount, rhs: PassportAccount) -> Bool { - if lhs.accountType != rhs.accountType { - return false - } - if lhs.xpub != rhs.xpub { - return false - } - if lhs.derivationPath != rhs.derivationPath { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(accountType) - hasher.combine(xpub) - hasher.combine(derivationPath) + hasher.combine(confirmations) + hasher.combine(inputs) + hasher.combine(outputs) + hasher.combine(size) + hasher.combine(vsize) + hasher.combine(weight) + hasher.combine(feeRate) } } -extension PassportAccount: Codable {} +extension TransactionDetail: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePassportAccount: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassportAccount { +public struct FfiConverterTypeTransactionDetail: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionDetail { return - try PassportAccount( - accountType: FfiConverterTypeAccountType.read(from: &buf), - xpub: FfiConverterString.read(from: &buf), - derivationPath: FfiConverterString.read(from: &buf) + try TransactionDetail( + txid: FfiConverterString.read(from: &buf), + received: FfiConverterUInt64.read(from: &buf), + sent: FfiConverterUInt64.read(from: &buf), + net: FfiConverterInt64.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + fee: FfiConverterOptionUInt64.read(from: &buf), + direction: FfiConverterTypeTxDirection.read(from: &buf), + blockHeight: FfiConverterOptionUInt32.read(from: &buf), + timestamp: FfiConverterOptionUInt64.read(from: &buf), + confirmations: FfiConverterUInt32.read(from: &buf), + inputs: FfiConverterSequenceTypeTxDetailInput.read(from: &buf), + outputs: FfiConverterSequenceTypeTxDetailOutput.read(from: &buf), + size: FfiConverterUInt32.read(from: &buf), + vsize: FfiConverterUInt32.read(from: &buf), + weight: FfiConverterUInt32.read(from: &buf), + feeRate: FfiConverterOptionDouble.read(from: &buf) ) } - public static func write(_ value: PassportAccount, into buf: inout [UInt8]) { - FfiConverterTypeAccountType.write(value.accountType, into: &buf) - FfiConverterString.write(value.xpub, into: &buf) - FfiConverterString.write(value.derivationPath, into: &buf) + public static func write(_ value: TransactionDetail, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt64.write(value.received, into: &buf) + FfiConverterUInt64.write(value.sent, into: &buf) + FfiConverterInt64.write(value.net, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterOptionUInt64.write(value.fee, into: &buf) + FfiConverterTypeTxDirection.write(value.direction, into: &buf) + FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) + FfiConverterOptionUInt64.write(value.timestamp, into: &buf) + FfiConverterUInt32.write(value.confirmations, into: &buf) + FfiConverterSequenceTypeTxDetailInput.write(value.inputs, into: &buf) + FfiConverterSequenceTypeTxDetailOutput.write(value.outputs, into: &buf) + FfiConverterUInt32.write(value.size, into: &buf) + FfiConverterUInt32.write(value.vsize, into: &buf) + FfiConverterUInt32.write(value.weight, into: &buf) + FfiConverterOptionDouble.write(value.feeRate, into: &buf) } } @@ -10536,88 +12828,132 @@ public struct FfiConverterTypePassportAccount: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassportAccount_lift(_ buf: RustBuffer) throws -> PassportAccount { - return try FfiConverterTypePassportAccount.lift(buf) +public func FfiConverterTypeTransactionDetail_lift(_ buf: RustBuffer) throws -> TransactionDetail { + return try FfiConverterTypeTransactionDetail.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassportAccount_lower(_ value: PassportAccount) -> RustBuffer { - return FfiConverterTypePassportAccount.lower(value) +public func FfiConverterTypeTransactionDetail_lower(_ value: TransactionDetail) -> RustBuffer { + return FfiConverterTypeTransactionDetail.lower(value) } /** - * The single-signature accounts exported by Passport for one account index. + * Details about an onchain transaction. */ -public struct PassportAccountExport { +public struct TransactionDetails { + public var walletId: String /** - * Root fingerprint used in descriptors and PSBT key origins. + * The transaction ID. */ - public var masterFingerprint: String - public var accountIndex: UInt32 - public var accounts: [PassportAccount] + public var txId: String + /** + * The net amount in this transaction (in satoshis). + * + * This is calculated as: (received - sent). For incoming payments, + * this will be positive. For outgoing payments, this will be negative. + * + * Note: This amount does NOT include transaction fees. + */ + public var amountSats: Int64 + /** + * The transaction inputs with full details. + */ + public var inputs: [TxInput] + /** + * The transaction outputs with full details. + */ + public var outputs: [TxOutput] // Default memberwise initializers are never public by default, so we // declare one manually. - public init( + public init(walletId: String, /** - * Root fingerprint used in descriptors and PSBT key origins. - */masterFingerprint: String, accountIndex: UInt32, accounts: [PassportAccount]) { - self.masterFingerprint = masterFingerprint - self.accountIndex = accountIndex - self.accounts = accounts + * The transaction ID. + */txId: String, + /** + * The net amount in this transaction (in satoshis). + * + * This is calculated as: (received - sent). For incoming payments, + * this will be positive. For outgoing payments, this will be negative. + * + * Note: This amount does NOT include transaction fees. + */amountSats: Int64, + /** + * The transaction inputs with full details. + */inputs: [TxInput], + /** + * The transaction outputs with full details. + */outputs: [TxOutput]) { + self.walletId = walletId + self.txId = txId + self.amountSats = amountSats + self.inputs = inputs + self.outputs = outputs } } #if compiler(>=6) -extension PassportAccountExport: Sendable {} +extension TransactionDetails: Sendable {} #endif -extension PassportAccountExport: Equatable, Hashable { - public static func ==(lhs: PassportAccountExport, rhs: PassportAccountExport) -> Bool { - if lhs.masterFingerprint != rhs.masterFingerprint { +extension TransactionDetails: Equatable, Hashable { + public static func ==(lhs: TransactionDetails, rhs: TransactionDetails) -> Bool { + if lhs.walletId != rhs.walletId { return false } - if lhs.accountIndex != rhs.accountIndex { + if lhs.txId != rhs.txId { return false } - if lhs.accounts != rhs.accounts { + if lhs.amountSats != rhs.amountSats { + return false + } + if lhs.inputs != rhs.inputs { + return false + } + if lhs.outputs != rhs.outputs { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(masterFingerprint) - hasher.combine(accountIndex) - hasher.combine(accounts) + hasher.combine(walletId) + hasher.combine(txId) + hasher.combine(amountSats) + hasher.combine(inputs) + hasher.combine(outputs) } } -extension PassportAccountExport: Codable {} +extension TransactionDetails: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePassportAccountExport: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassportAccountExport { +public struct FfiConverterTypeTransactionDetails: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionDetails { return - try PassportAccountExport( - masterFingerprint: FfiConverterString.read(from: &buf), - accountIndex: FfiConverterUInt32.read(from: &buf), - accounts: FfiConverterSequenceTypePassportAccount.read(from: &buf) + try TransactionDetails( + walletId: FfiConverterString.read(from: &buf), + txId: FfiConverterString.read(from: &buf), + amountSats: FfiConverterInt64.read(from: &buf), + inputs: FfiConverterSequenceTypeTxInput.read(from: &buf), + outputs: FfiConverterSequenceTypeTxOutput.read(from: &buf) ) } - public static func write(_ value: PassportAccountExport, into buf: inout [UInt8]) { - FfiConverterString.write(value.masterFingerprint, into: &buf) - FfiConverterUInt32.write(value.accountIndex, into: &buf) - FfiConverterSequenceTypePassportAccount.write(value.accounts, into: &buf) + public static func write(_ value: TransactionDetails, into buf: inout [UInt8]) { + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.txId, into: &buf) + FfiConverterInt64.write(value.amountSats, into: &buf) + FfiConverterSequenceTypeTxInput.write(value.inputs, into: &buf) + FfiConverterSequenceTypeTxOutput.write(value.outputs, into: &buf) } } @@ -10625,143 +12961,128 @@ public struct FfiConverterTypePassportAccountExport: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassportAccountExport_lift(_ buf: RustBuffer) throws -> PassportAccountExport { - return try FfiConverterTypePassportAccountExport.lift(buf) +public func FfiConverterTypeTransactionDetails_lift(_ buf: RustBuffer) throws -> TransactionDetails { + return try FfiConverterTypeTransactionDetails.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassportAccountExport_lower(_ value: PassportAccountExport) -> RustBuffer { - return FfiConverterTypePassportAccountExport.lower(value) +public func FfiConverterTypeTransactionDetails_lower(_ value: TransactionDetails) -> RustBuffer { + return FfiConverterTypeTransactionDetails.lower(value) } -public struct PreActivityMetadata { - public var walletId: String - public var paymentId: String - public var tags: [String] - public var paymentHash: String? - public var txId: String? - public var address: String? - public var isReceive: Bool - public var feeRate: UInt64 - public var isTransfer: Bool - public var channelId: String? - public var createdAt: UInt64 +/** + * Result from querying transaction history for an xpub. + */ +public struct TransactionHistoryResult { + /** + * All transactions, sorted: unconfirmed first, then by timestamp descending + */ + public var transactions: [HistoryTransaction] + /** + * Balance breakdown + */ + public var balance: WalletBalance + /** + * Total number of transactions + */ + public var txCount: UInt32 + /** + * Current blockchain tip height + */ + public var blockHeight: UInt32 + /** + * The detected or specified account type + */ + public var accountType: AccountType // Default memberwise initializers are never public by default, so we // declare one manually. - public init(walletId: String, paymentId: String, tags: [String], paymentHash: String?, txId: String?, address: String?, isReceive: Bool, feeRate: UInt64, isTransfer: Bool, channelId: String?, createdAt: UInt64) { - self.walletId = walletId - self.paymentId = paymentId - self.tags = tags - self.paymentHash = paymentHash - self.txId = txId - self.address = address - self.isReceive = isReceive - self.feeRate = feeRate - self.isTransfer = isTransfer - self.channelId = channelId - self.createdAt = createdAt + public init( + /** + * All transactions, sorted: unconfirmed first, then by timestamp descending + */transactions: [HistoryTransaction], + /** + * Balance breakdown + */balance: WalletBalance, + /** + * Total number of transactions + */txCount: UInt32, + /** + * Current blockchain tip height + */blockHeight: UInt32, + /** + * The detected or specified account type + */accountType: AccountType) { + self.transactions = transactions + self.balance = balance + self.txCount = txCount + self.blockHeight = blockHeight + self.accountType = accountType } } #if compiler(>=6) -extension PreActivityMetadata: Sendable {} +extension TransactionHistoryResult: Sendable {} #endif -extension PreActivityMetadata: Equatable, Hashable { - public static func ==(lhs: PreActivityMetadata, rhs: PreActivityMetadata) -> Bool { - if lhs.walletId != rhs.walletId { - return false - } - if lhs.paymentId != rhs.paymentId { - return false - } - if lhs.tags != rhs.tags { - return false - } - if lhs.paymentHash != rhs.paymentHash { - return false - } - if lhs.txId != rhs.txId { - return false - } - if lhs.address != rhs.address { - return false - } - if lhs.isReceive != rhs.isReceive { +extension TransactionHistoryResult: Equatable, Hashable { + public static func ==(lhs: TransactionHistoryResult, rhs: TransactionHistoryResult) -> Bool { + if lhs.transactions != rhs.transactions { return false } - if lhs.feeRate != rhs.feeRate { + if lhs.balance != rhs.balance { return false } - if lhs.isTransfer != rhs.isTransfer { + if lhs.txCount != rhs.txCount { return false } - if lhs.channelId != rhs.channelId { + if lhs.blockHeight != rhs.blockHeight { return false } - if lhs.createdAt != rhs.createdAt { + if lhs.accountType != rhs.accountType { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(walletId) - hasher.combine(paymentId) - hasher.combine(tags) - hasher.combine(paymentHash) - hasher.combine(txId) - hasher.combine(address) - hasher.combine(isReceive) - hasher.combine(feeRate) - hasher.combine(isTransfer) - hasher.combine(channelId) - hasher.combine(createdAt) + hasher.combine(transactions) + hasher.combine(balance) + hasher.combine(txCount) + hasher.combine(blockHeight) + hasher.combine(accountType) } } -extension PreActivityMetadata: Codable {} +extension TransactionHistoryResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePreActivityMetadata: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PreActivityMetadata { +public struct FfiConverterTypeTransactionHistoryResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionHistoryResult { return - try PreActivityMetadata( - walletId: FfiConverterString.read(from: &buf), - paymentId: FfiConverterString.read(from: &buf), - tags: FfiConverterSequenceString.read(from: &buf), - paymentHash: FfiConverterOptionString.read(from: &buf), - txId: FfiConverterOptionString.read(from: &buf), - address: FfiConverterOptionString.read(from: &buf), - isReceive: FfiConverterBool.read(from: &buf), - feeRate: FfiConverterUInt64.read(from: &buf), - isTransfer: FfiConverterBool.read(from: &buf), - channelId: FfiConverterOptionString.read(from: &buf), - createdAt: FfiConverterUInt64.read(from: &buf) + try TransactionHistoryResult( + transactions: FfiConverterSequenceTypeHistoryTransaction.read(from: &buf), + balance: FfiConverterTypeWalletBalance.read(from: &buf), + txCount: FfiConverterUInt32.read(from: &buf), + blockHeight: FfiConverterUInt32.read(from: &buf), + accountType: FfiConverterTypeAccountType.read(from: &buf) ) } - public static func write(_ value: PreActivityMetadata, into buf: inout [UInt8]) { - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.paymentId, into: &buf) - FfiConverterSequenceString.write(value.tags, into: &buf) - FfiConverterOptionString.write(value.paymentHash, into: &buf) - FfiConverterOptionString.write(value.txId, into: &buf) - FfiConverterOptionString.write(value.address, into: &buf) - FfiConverterBool.write(value.isReceive, into: &buf) - FfiConverterUInt64.write(value.feeRate, into: &buf) - FfiConverterBool.write(value.isTransfer, into: &buf) - FfiConverterOptionString.write(value.channelId, into: &buf) - FfiConverterUInt64.write(value.createdAt, into: &buf) + public static func write(_ value: TransactionHistoryResult, into buf: inout [UInt8]) { + FfiConverterSequenceTypeHistoryTransaction.write(value.transactions, into: &buf) + FfiConverterTypeWalletBalance.write(value.balance, into: &buf) + FfiConverterUInt32.write(value.txCount, into: &buf) + FfiConverterUInt32.write(value.blockHeight, into: &buf) + FfiConverterTypeAccountType.write(value.accountType, into: &buf) } } @@ -10769,63 +13090,86 @@ public struct FfiConverterTypePreActivityMetadata: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePreActivityMetadata_lift(_ buf: RustBuffer) throws -> PreActivityMetadata { - return try FfiConverterTypePreActivityMetadata.lift(buf) +public func FfiConverterTypeTransactionHistoryResult_lift(_ buf: RustBuffer) throws -> TransactionHistoryResult { + return try FfiConverterTypeTransactionHistoryResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePreActivityMetadata_lower(_ value: PreActivityMetadata) -> RustBuffer { - return FfiConverterTypePreActivityMetadata.lower(value) +public func FfiConverterTypeTransactionHistoryResult_lower(_ value: TransactionHistoryResult) -> RustBuffer { + return FfiConverterTypeTransactionHistoryResult.lower(value) } -public struct PubkyAuth { - public var data: String +/** + * Address response from device. + */ +public struct TrezorAddressResponse { + /** + * The Bitcoin address + */ + public var address: String + /** + * The serialized path (e.g., "m/84'/0'/0'/0/0") + */ + public var path: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(data: String) { - self.data = data + public init( + /** + * The Bitcoin address + */address: String, + /** + * The serialized path (e.g., "m/84'/0'/0'/0/0") + */path: String) { + self.address = address + self.path = path } } #if compiler(>=6) -extension PubkyAuth: Sendable {} +extension TrezorAddressResponse: Sendable {} #endif -extension PubkyAuth: Equatable, Hashable { - public static func ==(lhs: PubkyAuth, rhs: PubkyAuth) -> Bool { - if lhs.data != rhs.data { +extension TrezorAddressResponse: Equatable, Hashable { + public static func ==(lhs: TrezorAddressResponse, rhs: TrezorAddressResponse) -> Bool { + if lhs.address != rhs.address { + return false + } + if lhs.path != rhs.path { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(data) + hasher.combine(address) + hasher.combine(path) } } -extension PubkyAuth: Codable {} +extension TrezorAddressResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyAuth: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuth { +public struct FfiConverterTypeTrezorAddressResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorAddressResponse { return - try PubkyAuth( - data: FfiConverterString.read(from: &buf) + try TrezorAddressResponse( + address: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: PubkyAuth, into buf: inout [UInt8]) { - FfiConverterString.write(value.data, into: &buf) + public static func write(_ value: TrezorAddressResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.path, into: &buf) } } @@ -10833,128 +13177,128 @@ public struct FfiConverterTypePubkyAuth: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuth_lift(_ buf: RustBuffer) throws -> PubkyAuth { - return try FfiConverterTypePubkyAuth.lift(buf) +public func FfiConverterTypeTrezorAddressResponse_lift(_ buf: RustBuffer) throws -> TrezorAddressResponse { + return try FfiConverterTypeTrezorAddressResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuth_lower(_ value: PubkyAuth) -> RustBuffer { - return FfiConverterTypePubkyAuth.lower(value) +public func FfiConverterTypeTrezorAddressResponse_lower(_ value: TrezorAddressResponse) -> RustBuffer { + return FfiConverterTypeTrezorAddressResponse.lower(value) } /** - * Details extracted from a `pubkyauth://` deep-link URL. + * Result from a high-level message call (for BLE/THP devices) */ -public struct PubkyAuthDetails { +public struct TrezorCallMessageResult { /** - * Whether this is a signin or signup flow. + * Whether the call succeeded */ - public var kind: PubkyAuthKind + public var success: Bool /** - * Requested capabilities (e.g. `"/pub/pubky.app/:rw"`). + * Response message type */ - public var capabilities: String + public var messageType: UInt16 /** - * Relay URL used for the auth exchange. + * Response protobuf data */ - public var relay: String + public var data: Data /** - * Homeserver public key (z32-encoded). Present only for signup flows. + * Error message (empty on success) */ - public var homeserver: String? + public var error: String /** - * Signup token. Present only for signup flows. + * Structured error code (None on success or when the native error is generic) */ - public var signupToken: String? + public var errorCode: TrezorTransportErrorCode? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Whether this is a signin or signup flow. - */kind: PubkyAuthKind, + * Whether the call succeeded + */success: Bool, /** - * Requested capabilities (e.g. `"/pub/pubky.app/:rw"`). - */capabilities: String, + * Response message type + */messageType: UInt16, /** - * Relay URL used for the auth exchange. - */relay: String, + * Response protobuf data + */data: Data, /** - * Homeserver public key (z32-encoded). Present only for signup flows. - */homeserver: String?, + * Error message (empty on success) + */error: String, /** - * Signup token. Present only for signup flows. - */signupToken: String?) { - self.kind = kind - self.capabilities = capabilities - self.relay = relay - self.homeserver = homeserver - self.signupToken = signupToken + * Structured error code (None on success or when the native error is generic) + */errorCode: TrezorTransportErrorCode?) { + self.success = success + self.messageType = messageType + self.data = data + self.error = error + self.errorCode = errorCode } } #if compiler(>=6) -extension PubkyAuthDetails: Sendable {} +extension TrezorCallMessageResult: Sendable {} #endif -extension PubkyAuthDetails: Equatable, Hashable { - public static func ==(lhs: PubkyAuthDetails, rhs: PubkyAuthDetails) -> Bool { - if lhs.kind != rhs.kind { +extension TrezorCallMessageResult: Equatable, Hashable { + public static func ==(lhs: TrezorCallMessageResult, rhs: TrezorCallMessageResult) -> Bool { + if lhs.success != rhs.success { return false } - if lhs.capabilities != rhs.capabilities { + if lhs.messageType != rhs.messageType { return false } - if lhs.relay != rhs.relay { + if lhs.data != rhs.data { return false } - if lhs.homeserver != rhs.homeserver { + if lhs.error != rhs.error { return false } - if lhs.signupToken != rhs.signupToken { + if lhs.errorCode != rhs.errorCode { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(kind) - hasher.combine(capabilities) - hasher.combine(relay) - hasher.combine(homeserver) - hasher.combine(signupToken) + hasher.combine(success) + hasher.combine(messageType) + hasher.combine(data) + hasher.combine(error) + hasher.combine(errorCode) } } -extension PubkyAuthDetails: Codable {} +extension TrezorCallMessageResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyAuthDetails: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthDetails { +public struct FfiConverterTypeTrezorCallMessageResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCallMessageResult { return - try PubkyAuthDetails( - kind: FfiConverterTypePubkyAuthKind.read(from: &buf), - capabilities: FfiConverterString.read(from: &buf), - relay: FfiConverterString.read(from: &buf), - homeserver: FfiConverterOptionString.read(from: &buf), - signupToken: FfiConverterOptionString.read(from: &buf) + try TrezorCallMessageResult( + success: FfiConverterBool.read(from: &buf), + messageType: FfiConverterUInt16.read(from: &buf), + data: FfiConverterData.read(from: &buf), + error: FfiConverterString.read(from: &buf), + errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } - public static func write(_ value: PubkyAuthDetails, into buf: inout [UInt8]) { - FfiConverterTypePubkyAuthKind.write(value.kind, into: &buf) - FfiConverterString.write(value.capabilities, into: &buf) - FfiConverterString.write(value.relay, into: &buf) - FfiConverterOptionString.write(value.homeserver, into: &buf) - FfiConverterOptionString.write(value.signupToken, into: &buf) + public static func write(_ value: TrezorCallMessageResult, into buf: inout [UInt8]) { + FfiConverterBool.write(value.success, into: &buf) + FfiConverterUInt16.write(value.messageType, into: &buf) + FfiConverterData.write(value.data, into: &buf) + FfiConverterString.write(value.error, into: &buf) + FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) } } @@ -10962,95 +13306,156 @@ public struct FfiConverterTypePubkyAuthDetails: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuthDetails_lift(_ buf: RustBuffer) throws -> PubkyAuthDetails { - return try FfiConverterTypePubkyAuthDetails.lift(buf) +public func FfiConverterTypeTrezorCallMessageResult_lift(_ buf: RustBuffer) throws -> TrezorCallMessageResult { + return try FfiConverterTypeTrezorCallMessageResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuthDetails_lower(_ value: PubkyAuthDetails) -> RustBuffer { - return FfiConverterTypePubkyAuthDetails.lower(value) +public func FfiConverterTypeTrezorCallMessageResult_lower(_ value: TrezorCallMessageResult) -> RustBuffer { + return FfiConverterTypeTrezorCallMessageResult.lower(value) } -public struct PubkyProfile { - public var name: String - public var bio: String? - public var image: String? - public var links: [PubkyProfileLink]? - public var status: String? +/** + * Device information exposed to FFI. + */ +public struct TrezorDeviceInfo { + /** + * Unique identifier for the device + */ + public var id: String + /** + * Transport type (USB or Bluetooth) + */ + public var transportType: TrezorTransportType + /** + * Device name (from BLE advertisement or USB descriptor) + */ + public var name: String? + /** + * Transport-specific path (used internally for connection) + */ + public var path: String + /** + * Device label (set by user during device setup) + */ + public var label: String? + /** + * Device model (e.g., "T2", "Safe 5", "Safe 7") + */ + public var model: String? + /** + * Whether the device is in bootloader mode + */ + public var isBootloader: Bool // Default memberwise initializers are never public by default, so we // declare one manually. - public init(name: String, bio: String?, image: String?, links: [PubkyProfileLink]?, status: String?) { + public init( + /** + * Unique identifier for the device + */id: String, + /** + * Transport type (USB or Bluetooth) + */transportType: TrezorTransportType, + /** + * Device name (from BLE advertisement or USB descriptor) + */name: String?, + /** + * Transport-specific path (used internally for connection) + */path: String, + /** + * Device label (set by user during device setup) + */label: String?, + /** + * Device model (e.g., "T2", "Safe 5", "Safe 7") + */model: String?, + /** + * Whether the device is in bootloader mode + */isBootloader: Bool) { + self.id = id + self.transportType = transportType self.name = name - self.bio = bio - self.image = image - self.links = links - self.status = status + self.path = path + self.label = label + self.model = model + self.isBootloader = isBootloader } } #if compiler(>=6) -extension PubkyProfile: Sendable {} +extension TrezorDeviceInfo: Sendable {} #endif -extension PubkyProfile: Equatable, Hashable { - public static func ==(lhs: PubkyProfile, rhs: PubkyProfile) -> Bool { +extension TrezorDeviceInfo: Equatable, Hashable { + public static func ==(lhs: TrezorDeviceInfo, rhs: TrezorDeviceInfo) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.transportType != rhs.transportType { + return false + } if lhs.name != rhs.name { return false } - if lhs.bio != rhs.bio { + if lhs.path != rhs.path { return false } - if lhs.image != rhs.image { + if lhs.label != rhs.label { return false } - if lhs.links != rhs.links { + if lhs.model != rhs.model { return false } - if lhs.status != rhs.status { + if lhs.isBootloader != rhs.isBootloader { return false } return true } public func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(transportType) hasher.combine(name) - hasher.combine(bio) - hasher.combine(image) - hasher.combine(links) - hasher.combine(status) + hasher.combine(path) + hasher.combine(label) + hasher.combine(model) + hasher.combine(isBootloader) } } -extension PubkyProfile: Codable {} +extension TrezorDeviceInfo: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyProfile: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyProfile { +public struct FfiConverterTypeTrezorDeviceInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorDeviceInfo { return - try PubkyProfile( - name: FfiConverterString.read(from: &buf), - bio: FfiConverterOptionString.read(from: &buf), - image: FfiConverterOptionString.read(from: &buf), - links: FfiConverterOptionSequenceTypePubkyProfileLink.read(from: &buf), - status: FfiConverterOptionString.read(from: &buf) + try TrezorDeviceInfo( + id: FfiConverterString.read(from: &buf), + transportType: FfiConverterTypeTrezorTransportType.read(from: &buf), + name: FfiConverterOptionString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + label: FfiConverterOptionString.read(from: &buf), + model: FfiConverterOptionString.read(from: &buf), + isBootloader: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: PubkyProfile, into buf: inout [UInt8]) { - FfiConverterString.write(value.name, into: &buf) - FfiConverterOptionString.write(value.bio, into: &buf) - FfiConverterOptionString.write(value.image, into: &buf) - FfiConverterOptionSequenceTypePubkyProfileLink.write(value.links, into: &buf) - FfiConverterOptionString.write(value.status, into: &buf) + public static func write(_ value: TrezorDeviceInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterTypeTrezorTransportType.write(value.transportType, into: &buf) + FfiConverterOptionString.write(value.name, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterOptionString.write(value.label, into: &buf) + FfiConverterOptionString.write(value.model, into: &buf) + FfiConverterBool.write(value.isBootloader, into: &buf) } } @@ -11058,71 +13463,246 @@ public struct FfiConverterTypePubkyProfile: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyProfile_lift(_ buf: RustBuffer) throws -> PubkyProfile { - return try FfiConverterTypePubkyProfile.lift(buf) +public func FfiConverterTypeTrezorDeviceInfo_lift(_ buf: RustBuffer) throws -> TrezorDeviceInfo { + return try FfiConverterTypeTrezorDeviceInfo.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyProfile_lower(_ value: PubkyProfile) -> RustBuffer { - return FfiConverterTypePubkyProfile.lower(value) +public func FfiConverterTypeTrezorDeviceInfo_lower(_ value: TrezorDeviceInfo) -> RustBuffer { + return FfiConverterTypeTrezorDeviceInfo.lower(value) } -public struct PubkyProfileLink { - public var title: String - public var url: String +/** + * Device features after initialization. + */ +public struct TrezorFeatures { + /** + * Vendor string + */ + public var vendor: String? + /** + * Device model + */ + public var model: String? + /** + * Device label (set by user during device setup) + */ + public var label: String? + /** + * Device ID (unique per device) + */ + public var deviceId: String? + /** + * Major firmware version + */ + public var majorVersion: UInt32? + /** + * Minor firmware version + */ + public var minorVersion: UInt32? + /** + * Patch firmware version + */ + public var patchVersion: UInt32? + /** + * Whether PIN protection is enabled + */ + public var pinProtection: Bool? + /** + * Whether the device is currently unlocked. When PIN protection is enabled + * and this is `Some(false)`, mobile callers should back off and ask the + * user to unlock the Trezor instead of repeatedly reconnecting. + */ + public var unlocked: Bool? + /** + * Whether passphrase protection is enabled + */ + public var passphraseProtection: Bool? + /** + * Whether the device is initialized with a seed + */ + public var initialized: Bool? + /** + * Whether the device needs backup + */ + public var needsBackup: Bool? + /** + * Whether the device can accept passphrase entry on the device itself + * (`Capability_PassphraseEntry`). When false/None, use host entry only. + */ + public var passphraseEntryCapable: Bool? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(title: String, url: String) { - self.title = title - self.url = url + public init( + /** + * Vendor string + */vendor: String?, + /** + * Device model + */model: String?, + /** + * Device label (set by user during device setup) + */label: String?, + /** + * Device ID (unique per device) + */deviceId: String?, + /** + * Major firmware version + */majorVersion: UInt32?, + /** + * Minor firmware version + */minorVersion: UInt32?, + /** + * Patch firmware version + */patchVersion: UInt32?, + /** + * Whether PIN protection is enabled + */pinProtection: Bool?, + /** + * Whether the device is currently unlocked. When PIN protection is enabled + * and this is `Some(false)`, mobile callers should back off and ask the + * user to unlock the Trezor instead of repeatedly reconnecting. + */unlocked: Bool?, + /** + * Whether passphrase protection is enabled + */passphraseProtection: Bool?, + /** + * Whether the device is initialized with a seed + */initialized: Bool?, + /** + * Whether the device needs backup + */needsBackup: Bool?, + /** + * Whether the device can accept passphrase entry on the device itself + * (`Capability_PassphraseEntry`). When false/None, use host entry only. + */passphraseEntryCapable: Bool?) { + self.vendor = vendor + self.model = model + self.label = label + self.deviceId = deviceId + self.majorVersion = majorVersion + self.minorVersion = minorVersion + self.patchVersion = patchVersion + self.pinProtection = pinProtection + self.unlocked = unlocked + self.passphraseProtection = passphraseProtection + self.initialized = initialized + self.needsBackup = needsBackup + self.passphraseEntryCapable = passphraseEntryCapable } } #if compiler(>=6) -extension PubkyProfileLink: Sendable {} +extension TrezorFeatures: Sendable {} #endif -extension PubkyProfileLink: Equatable, Hashable { - public static func ==(lhs: PubkyProfileLink, rhs: PubkyProfileLink) -> Bool { - if lhs.title != rhs.title { +extension TrezorFeatures: Equatable, Hashable { + public static func ==(lhs: TrezorFeatures, rhs: TrezorFeatures) -> Bool { + if lhs.vendor != rhs.vendor { return false } - if lhs.url != rhs.url { + if lhs.model != rhs.model { + return false + } + if lhs.label != rhs.label { + return false + } + if lhs.deviceId != rhs.deviceId { + return false + } + if lhs.majorVersion != rhs.majorVersion { + return false + } + if lhs.minorVersion != rhs.minorVersion { + return false + } + if lhs.patchVersion != rhs.patchVersion { + return false + } + if lhs.pinProtection != rhs.pinProtection { + return false + } + if lhs.unlocked != rhs.unlocked { + return false + } + if lhs.passphraseProtection != rhs.passphraseProtection { + return false + } + if lhs.initialized != rhs.initialized { + return false + } + if lhs.needsBackup != rhs.needsBackup { + return false + } + if lhs.passphraseEntryCapable != rhs.passphraseEntryCapable { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(title) - hasher.combine(url) + hasher.combine(vendor) + hasher.combine(model) + hasher.combine(label) + hasher.combine(deviceId) + hasher.combine(majorVersion) + hasher.combine(minorVersion) + hasher.combine(patchVersion) + hasher.combine(pinProtection) + hasher.combine(unlocked) + hasher.combine(passphraseProtection) + hasher.combine(initialized) + hasher.combine(needsBackup) + hasher.combine(passphraseEntryCapable) } } -extension PubkyProfileLink: Codable {} +extension TrezorFeatures: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyProfileLink: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyProfileLink { +public struct FfiConverterTypeTrezorFeatures: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorFeatures { return - try PubkyProfileLink( - title: FfiConverterString.read(from: &buf), - url: FfiConverterString.read(from: &buf) + try TrezorFeatures( + vendor: FfiConverterOptionString.read(from: &buf), + model: FfiConverterOptionString.read(from: &buf), + label: FfiConverterOptionString.read(from: &buf), + deviceId: FfiConverterOptionString.read(from: &buf), + majorVersion: FfiConverterOptionUInt32.read(from: &buf), + minorVersion: FfiConverterOptionUInt32.read(from: &buf), + patchVersion: FfiConverterOptionUInt32.read(from: &buf), + pinProtection: FfiConverterOptionBool.read(from: &buf), + unlocked: FfiConverterOptionBool.read(from: &buf), + passphraseProtection: FfiConverterOptionBool.read(from: &buf), + initialized: FfiConverterOptionBool.read(from: &buf), + needsBackup: FfiConverterOptionBool.read(from: &buf), + passphraseEntryCapable: FfiConverterOptionBool.read(from: &buf) ) } - public static func write(_ value: PubkyProfileLink, into buf: inout [UInt8]) { - FfiConverterString.write(value.title, into: &buf) - FfiConverterString.write(value.url, into: &buf) + public static func write(_ value: TrezorFeatures, into buf: inout [UInt8]) { + FfiConverterOptionString.write(value.vendor, into: &buf) + FfiConverterOptionString.write(value.model, into: &buf) + FfiConverterOptionString.write(value.label, into: &buf) + FfiConverterOptionString.write(value.deviceId, into: &buf) + FfiConverterOptionUInt32.write(value.majorVersion, into: &buf) + FfiConverterOptionUInt32.write(value.minorVersion, into: &buf) + FfiConverterOptionUInt32.write(value.patchVersion, into: &buf) + FfiConverterOptionBool.write(value.pinProtection, into: &buf) + FfiConverterOptionBool.write(value.unlocked, into: &buf) + FfiConverterOptionBool.write(value.passphraseProtection, into: &buf) + FfiConverterOptionBool.write(value.initialized, into: &buf) + FfiConverterOptionBool.write(value.needsBackup, into: &buf) + FfiConverterOptionBool.write(value.passphraseEntryCapable, into: &buf) } } @@ -11130,125 +13710,114 @@ public struct FfiConverterTypePubkyProfileLink: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyProfileLink_lift(_ buf: RustBuffer) throws -> PubkyProfileLink { - return try FfiConverterTypePubkyProfileLink.lift(buf) +public func FfiConverterTypeTrezorFeatures_lift(_ buf: RustBuffer) throws -> TrezorFeatures { + return try FfiConverterTypeTrezorFeatures.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyProfileLink_lower(_ value: PubkyProfileLink) -> RustBuffer { - return FfiConverterTypePubkyProfileLink.lower(value) +public func FfiConverterTypeTrezorFeatures_lower(_ value: TrezorFeatures) -> RustBuffer { + return FfiConverterTypeTrezorFeatures.lower(value) } /** - * Result of creating a reverse swap (Lightning -> onchain). - * - * The caller pays `invoice` from its Lightning node; once Boltz locks funds at - * `lockup_address`, the module claims them to the provided onchain address. + * Parameters for getting an address from the device. */ -public struct ReverseSwapResponse { - public var id: String +public struct TrezorGetAddressParams { /** - * Hold invoice the caller must pay via Lightning. + * BIP32 path (e.g., "m/84'/0'/0'/0/0") */ - public var invoice: String + public var path: String /** - * Address Boltz locks the onchain funds to. + * Coin network (default: Bitcoin) */ - public var lockupAddress: String + public var coin: TrezorCoinType? /** - * Amount in satoshis that will be received onchain (after Boltz fees). + * Whether to display the address on the device for confirmation */ - public var onchainAmountSat: UInt64 + public var showOnTrezor: Bool /** - * Onchain timeout height for the swap. + * Script type (auto-detected from path if not specified) */ - public var timeoutBlockHeight: UInt64 + public var scriptType: TrezorScriptType? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, + public init( /** - * Hold invoice the caller must pay via Lightning. - */invoice: String, + * BIP32 path (e.g., "m/84'/0'/0'/0/0") + */path: String, /** - * Address Boltz locks the onchain funds to. - */lockupAddress: String, + * Coin network (default: Bitcoin) + */coin: TrezorCoinType?, /** - * Amount in satoshis that will be received onchain (after Boltz fees). - */onchainAmountSat: UInt64, + * Whether to display the address on the device for confirmation + */showOnTrezor: Bool, /** - * Onchain timeout height for the swap. - */timeoutBlockHeight: UInt64) { - self.id = id - self.invoice = invoice - self.lockupAddress = lockupAddress - self.onchainAmountSat = onchainAmountSat - self.timeoutBlockHeight = timeoutBlockHeight + * Script type (auto-detected from path if not specified) + */scriptType: TrezorScriptType?) { + self.path = path + self.coin = coin + self.showOnTrezor = showOnTrezor + self.scriptType = scriptType } } #if compiler(>=6) -extension ReverseSwapResponse: Sendable {} +extension TrezorGetAddressParams: Sendable {} #endif -extension ReverseSwapResponse: Equatable, Hashable { - public static func ==(lhs: ReverseSwapResponse, rhs: ReverseSwapResponse) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.invoice != rhs.invoice { +extension TrezorGetAddressParams: Equatable, Hashable { + public static func ==(lhs: TrezorGetAddressParams, rhs: TrezorGetAddressParams) -> Bool { + if lhs.path != rhs.path { return false } - if lhs.lockupAddress != rhs.lockupAddress { + if lhs.coin != rhs.coin { return false } - if lhs.onchainAmountSat != rhs.onchainAmountSat { + if lhs.showOnTrezor != rhs.showOnTrezor { return false } - if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { + if lhs.scriptType != rhs.scriptType { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(invoice) - hasher.combine(lockupAddress) - hasher.combine(onchainAmountSat) - hasher.combine(timeoutBlockHeight) + hasher.combine(path) + hasher.combine(coin) + hasher.combine(showOnTrezor) + hasher.combine(scriptType) } } -extension ReverseSwapResponse: Codable {} +extension TrezorGetAddressParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeReverseSwapResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ReverseSwapResponse { +public struct FfiConverterTypeTrezorGetAddressParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorGetAddressParams { return - try ReverseSwapResponse( - id: FfiConverterString.read(from: &buf), - invoice: FfiConverterString.read(from: &buf), - lockupAddress: FfiConverterString.read(from: &buf), - onchainAmountSat: FfiConverterUInt64.read(from: &buf), - timeoutBlockHeight: FfiConverterUInt64.read(from: &buf) + try TrezorGetAddressParams( + path: FfiConverterString.read(from: &buf), + coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), + showOnTrezor: FfiConverterBool.read(from: &buf), + scriptType: FfiConverterOptionTypeTrezorScriptType.read(from: &buf) ) } - public static func write(_ value: ReverseSwapResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.invoice, into: &buf) - FfiConverterString.write(value.lockupAddress, into: &buf) - FfiConverterUInt64.write(value.onchainAmountSat, into: &buf) - FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) + public static func write(_ value: TrezorGetAddressParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) + FfiConverterBool.write(value.showOnTrezor, into: &buf) + FfiConverterOptionTypeTrezorScriptType.write(value.scriptType, into: &buf) } } @@ -11256,128 +13825,100 @@ public struct FfiConverterTypeReverseSwapResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeReverseSwapResponse_lift(_ buf: RustBuffer) throws -> ReverseSwapResponse { - return try FfiConverterTypeReverseSwapResponse.lift(buf) +public func FfiConverterTypeTrezorGetAddressParams_lift(_ buf: RustBuffer) throws -> TrezorGetAddressParams { + return try FfiConverterTypeTrezorGetAddressParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeReverseSwapResponse_lower(_ value: ReverseSwapResponse) -> RustBuffer { - return FfiConverterTypeReverseSwapResponse.lower(value) +public func FfiConverterTypeTrezorGetAddressParams_lower(_ value: TrezorGetAddressParams) -> RustBuffer { + return FfiConverterTypeTrezorGetAddressParams.lower(value) } /** - * Result from querying a single Bitcoin address. + * Parameters for getting a public key from the device. */ -public struct SingleAddressInfoResult { - /** - * The queried address - */ - public var address: String - /** - * Total confirmed balance in satoshis - */ - public var balance: UInt64 +public struct TrezorGetPublicKeyParams { /** - * UTXOs for this address + * BIP32 path (e.g., "m/84'/0'/0'") */ - public var utxos: [AccountUtxo] + public var path: String /** - * Number of transactions involving this address + * Coin network (default: Bitcoin) */ - public var transfers: UInt32 + public var coin: TrezorCoinType? /** - * Current blockchain tip height + * Whether to display on device for confirmation */ - public var blockHeight: UInt32 + public var showOnTrezor: Bool // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * The queried address - */address: String, - /** - * Total confirmed balance in satoshis - */balance: UInt64, - /** - * UTXOs for this address - */utxos: [AccountUtxo], - /** - * Number of transactions involving this address - */transfers: UInt32, + * BIP32 path (e.g., "m/84'/0'/0'") + */path: String, /** - * Current blockchain tip height - */blockHeight: UInt32) { - self.address = address - self.balance = balance - self.utxos = utxos - self.transfers = transfers - self.blockHeight = blockHeight + * Coin network (default: Bitcoin) + */coin: TrezorCoinType?, + /** + * Whether to display on device for confirmation + */showOnTrezor: Bool) { + self.path = path + self.coin = coin + self.showOnTrezor = showOnTrezor } } #if compiler(>=6) -extension SingleAddressInfoResult: Sendable {} +extension TrezorGetPublicKeyParams: Sendable {} #endif -extension SingleAddressInfoResult: Equatable, Hashable { - public static func ==(lhs: SingleAddressInfoResult, rhs: SingleAddressInfoResult) -> Bool { - if lhs.address != rhs.address { - return false - } - if lhs.balance != rhs.balance { - return false - } - if lhs.utxos != rhs.utxos { +extension TrezorGetPublicKeyParams: Equatable, Hashable { + public static func ==(lhs: TrezorGetPublicKeyParams, rhs: TrezorGetPublicKeyParams) -> Bool { + if lhs.path != rhs.path { return false } - if lhs.transfers != rhs.transfers { + if lhs.coin != rhs.coin { return false } - if lhs.blockHeight != rhs.blockHeight { + if lhs.showOnTrezor != rhs.showOnTrezor { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(balance) - hasher.combine(utxos) - hasher.combine(transfers) - hasher.combine(blockHeight) + hasher.combine(path) + hasher.combine(coin) + hasher.combine(showOnTrezor) } } -extension SingleAddressInfoResult: Codable {} +extension TrezorGetPublicKeyParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSingleAddressInfoResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SingleAddressInfoResult { +public struct FfiConverterTypeTrezorGetPublicKeyParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorGetPublicKeyParams { return - try SingleAddressInfoResult( - address: FfiConverterString.read(from: &buf), - balance: FfiConverterUInt64.read(from: &buf), - utxos: FfiConverterSequenceTypeAccountUtxo.read(from: &buf), - transfers: FfiConverterUInt32.read(from: &buf), - blockHeight: FfiConverterUInt32.read(from: &buf) + try TrezorGetPublicKeyParams( + path: FfiConverterString.read(from: &buf), + coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), + showOnTrezor: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: SingleAddressInfoResult, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterUInt64.write(value.balance, into: &buf) - FfiConverterSequenceTypeAccountUtxo.write(value.utxos, into: &buf) - FfiConverterUInt32.write(value.transfers, into: &buf) - FfiConverterUInt32.write(value.blockHeight, into: &buf) + public static func write(_ value: TrezorGetPublicKeyParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) + FfiConverterBool.write(value.showOnTrezor, into: &buf) } } @@ -11385,139 +13926,128 @@ public struct FfiConverterTypeSingleAddressInfoResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSingleAddressInfoResult_lift(_ buf: RustBuffer) throws -> SingleAddressInfoResult { - return try FfiConverterTypeSingleAddressInfoResult.lift(buf) +public func FfiConverterTypeTrezorGetPublicKeyParams_lift(_ buf: RustBuffer) throws -> TrezorGetPublicKeyParams { + return try FfiConverterTypeTrezorGetPublicKeyParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSingleAddressInfoResult_lower(_ value: SingleAddressInfoResult) -> RustBuffer { - return FfiConverterTypeSingleAddressInfoResult.lower(value) +public func FfiConverterTypeTrezorGetPublicKeyParams_lower(_ value: TrezorGetPublicKeyParams) -> RustBuffer { + return FfiConverterTypeTrezorGetPublicKeyParams.lower(value) } /** - * Result of creating a submarine swap (onchain -> Lightning). - * - * The caller funds `address` with `expected_amount_sat` from its onchain - * wallet; Boltz then pays the Lightning invoice supplied at creation. + * Previous transaction data (for non-SegWit input verification). */ -public struct SubmarineSwapResponse { - public var id: String +public struct TrezorPrevTx { /** - * Onchain lockup address to send funds to. + * Transaction hash (hex encoded) */ - public var address: String + public var hash: String /** - * BIP21 URI for the lockup payment. + * Transaction version */ - public var bip21: String + public var version: UInt32 /** - * Exact amount in satoshis the caller must send to `address`. + * Lock time */ - public var expectedAmountSat: UInt64 + public var lockTime: UInt32 /** - * Whether Boltz will accept a zero-conf lockup. + * Transaction inputs */ - public var acceptZeroConf: Bool + public var inputs: [TrezorPrevTxInput] /** - * Onchain timeout height after which a refund is possible. + * Transaction outputs */ - public var timeoutBlockHeight: UInt64 + public var outputs: [TrezorPrevTxOutput] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, + public init( /** - * Onchain lockup address to send funds to. - */address: String, + * Transaction hash (hex encoded) + */hash: String, /** - * BIP21 URI for the lockup payment. - */bip21: String, + * Transaction version + */version: UInt32, /** - * Exact amount in satoshis the caller must send to `address`. - */expectedAmountSat: UInt64, + * Lock time + */lockTime: UInt32, /** - * Whether Boltz will accept a zero-conf lockup. - */acceptZeroConf: Bool, + * Transaction inputs + */inputs: [TrezorPrevTxInput], /** - * Onchain timeout height after which a refund is possible. - */timeoutBlockHeight: UInt64) { - self.id = id - self.address = address - self.bip21 = bip21 - self.expectedAmountSat = expectedAmountSat - self.acceptZeroConf = acceptZeroConf - self.timeoutBlockHeight = timeoutBlockHeight + * Transaction outputs + */outputs: [TrezorPrevTxOutput]) { + self.hash = hash + self.version = version + self.lockTime = lockTime + self.inputs = inputs + self.outputs = outputs } } #if compiler(>=6) -extension SubmarineSwapResponse: Sendable {} +extension TrezorPrevTx: Sendable {} #endif -extension SubmarineSwapResponse: Equatable, Hashable { - public static func ==(lhs: SubmarineSwapResponse, rhs: SubmarineSwapResponse) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.address != rhs.address { +extension TrezorPrevTx: Equatable, Hashable { + public static func ==(lhs: TrezorPrevTx, rhs: TrezorPrevTx) -> Bool { + if lhs.hash != rhs.hash { return false } - if lhs.bip21 != rhs.bip21 { + if lhs.version != rhs.version { return false } - if lhs.expectedAmountSat != rhs.expectedAmountSat { + if lhs.lockTime != rhs.lockTime { return false } - if lhs.acceptZeroConf != rhs.acceptZeroConf { + if lhs.inputs != rhs.inputs { return false } - if lhs.timeoutBlockHeight != rhs.timeoutBlockHeight { + if lhs.outputs != rhs.outputs { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(address) - hasher.combine(bip21) - hasher.combine(expectedAmountSat) - hasher.combine(acceptZeroConf) - hasher.combine(timeoutBlockHeight) + hasher.combine(hash) + hasher.combine(version) + hasher.combine(lockTime) + hasher.combine(inputs) + hasher.combine(outputs) } } -extension SubmarineSwapResponse: Codable {} +extension TrezorPrevTx: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSubmarineSwapResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SubmarineSwapResponse { +public struct FfiConverterTypeTrezorPrevTx: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTx { return - try SubmarineSwapResponse( - id: FfiConverterString.read(from: &buf), - address: FfiConverterString.read(from: &buf), - bip21: FfiConverterString.read(from: &buf), - expectedAmountSat: FfiConverterUInt64.read(from: &buf), - acceptZeroConf: FfiConverterBool.read(from: &buf), - timeoutBlockHeight: FfiConverterUInt64.read(from: &buf) + try TrezorPrevTx( + hash: FfiConverterString.read(from: &buf), + version: FfiConverterUInt32.read(from: &buf), + lockTime: FfiConverterUInt32.read(from: &buf), + inputs: FfiConverterSequenceTypeTrezorPrevTxInput.read(from: &buf), + outputs: FfiConverterSequenceTypeTrezorPrevTxOutput.read(from: &buf) ) } - public static func write(_ value: SubmarineSwapResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.bip21, into: &buf) - FfiConverterUInt64.write(value.expectedAmountSat, into: &buf) - FfiConverterBool.write(value.acceptZeroConf, into: &buf) - FfiConverterUInt64.write(value.timeoutBlockHeight, into: &buf) + public static func write(_ value: TrezorPrevTx, into buf: inout [UInt8]) { + FfiConverterString.write(value.hash, into: &buf) + FfiConverterUInt32.write(value.version, into: &buf) + FfiConverterUInt32.write(value.lockTime, into: &buf) + FfiConverterSequenceTypeTrezorPrevTxInput.write(value.inputs, into: &buf) + FfiConverterSequenceTypeTrezorPrevTxOutput.write(value.outputs, into: &buf) } } @@ -11525,122 +14055,114 @@ public struct FfiConverterTypeSubmarineSwapResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSubmarineSwapResponse_lift(_ buf: RustBuffer) throws -> SubmarineSwapResponse { - return try FfiConverterTypeSubmarineSwapResponse.lift(buf) +public func FfiConverterTypeTrezorPrevTx_lift(_ buf: RustBuffer) throws -> TrezorPrevTx { + return try FfiConverterTypeTrezorPrevTx.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSubmarineSwapResponse_lower(_ value: SubmarineSwapResponse) -> RustBuffer { - return FfiConverterTypeSubmarineSwapResponse.lower(value) +public func FfiConverterTypeTrezorPrevTx_lower(_ value: TrezorPrevTx) -> RustBuffer { + return FfiConverterTypeTrezorPrevTx.lower(value) } /** - * A hardware-wallet model Bitkit supports. + * Input of a previous transaction. */ -public struct SupportedHardwareWallet { - public var vendor: HardwareWalletVendor +public struct TrezorPrevTxInput { /** - * Human-readable manufacturer name, e.g. "Foundation". + * Previous transaction hash (hex encoded) */ - public var vendorName: String + public var prevHash: String /** - * Stable model identifier that applications can map to bundled assets. + * Previous output index */ - public var model: String + public var prevIndex: UInt32 /** - * Full user-facing name. + * Script signature (hex encoded) */ - public var displayName: String + public var scriptSig: String /** - * Transports over which the application can interact with this model. + * Sequence number */ - public var transports: [HardwareWalletTransport] + public var sequence: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(vendor: HardwareWalletVendor, + public init( /** - * Human-readable manufacturer name, e.g. "Foundation". - */vendorName: String, + * Previous transaction hash (hex encoded) + */prevHash: String, /** - * Stable model identifier that applications can map to bundled assets. - */model: String, + * Previous output index + */prevIndex: UInt32, /** - * Full user-facing name. - */displayName: String, + * Script signature (hex encoded) + */scriptSig: String, /** - * Transports over which the application can interact with this model. - */transports: [HardwareWalletTransport]) { - self.vendor = vendor - self.vendorName = vendorName - self.model = model - self.displayName = displayName - self.transports = transports + * Sequence number + */sequence: UInt32) { + self.prevHash = prevHash + self.prevIndex = prevIndex + self.scriptSig = scriptSig + self.sequence = sequence } } #if compiler(>=6) -extension SupportedHardwareWallet: Sendable {} +extension TrezorPrevTxInput: Sendable {} #endif -extension SupportedHardwareWallet: Equatable, Hashable { - public static func ==(lhs: SupportedHardwareWallet, rhs: SupportedHardwareWallet) -> Bool { - if lhs.vendor != rhs.vendor { - return false - } - if lhs.vendorName != rhs.vendorName { +extension TrezorPrevTxInput: Equatable, Hashable { + public static func ==(lhs: TrezorPrevTxInput, rhs: TrezorPrevTxInput) -> Bool { + if lhs.prevHash != rhs.prevHash { return false } - if lhs.model != rhs.model { + if lhs.prevIndex != rhs.prevIndex { return false } - if lhs.displayName != rhs.displayName { + if lhs.scriptSig != rhs.scriptSig { return false } - if lhs.transports != rhs.transports { + if lhs.sequence != rhs.sequence { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(vendor) - hasher.combine(vendorName) - hasher.combine(model) - hasher.combine(displayName) - hasher.combine(transports) + hasher.combine(prevHash) + hasher.combine(prevIndex) + hasher.combine(scriptSig) + hasher.combine(sequence) } } -extension SupportedHardwareWallet: Codable {} +extension TrezorPrevTxInput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSupportedHardwareWallet: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SupportedHardwareWallet { +public struct FfiConverterTypeTrezorPrevTxInput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTxInput { return - try SupportedHardwareWallet( - vendor: FfiConverterTypeHardwareWalletVendor.read(from: &buf), - vendorName: FfiConverterString.read(from: &buf), - model: FfiConverterString.read(from: &buf), - displayName: FfiConverterString.read(from: &buf), - transports: FfiConverterSequenceTypeHardwareWalletTransport.read(from: &buf) + try TrezorPrevTxInput( + prevHash: FfiConverterString.read(from: &buf), + prevIndex: FfiConverterUInt32.read(from: &buf), + scriptSig: FfiConverterString.read(from: &buf), + sequence: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: SupportedHardwareWallet, into buf: inout [UInt8]) { - FfiConverterTypeHardwareWalletVendor.write(value.vendor, into: &buf) - FfiConverterString.write(value.vendorName, into: &buf) - FfiConverterString.write(value.model, into: &buf) - FfiConverterString.write(value.displayName, into: &buf) - FfiConverterSequenceTypeHardwareWalletTransport.write(value.transports, into: &buf) + public static func write(_ value: TrezorPrevTxInput, into buf: inout [UInt8]) { + FfiConverterString.write(value.prevHash, into: &buf) + FfiConverterUInt32.write(value.prevIndex, into: &buf) + FfiConverterString.write(value.scriptSig, into: &buf) + FfiConverterUInt32.write(value.sequence, into: &buf) } } @@ -11648,111 +14170,86 @@ public struct FfiConverterTypeSupportedHardwareWallet: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSupportedHardwareWallet_lift(_ buf: RustBuffer) throws -> SupportedHardwareWallet { - return try FfiConverterTypeSupportedHardwareWallet.lift(buf) +public func FfiConverterTypeTrezorPrevTxInput_lift(_ buf: RustBuffer) throws -> TrezorPrevTxInput { + return try FfiConverterTypeTrezorPrevTxInput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSupportedHardwareWallet_lower(_ value: SupportedHardwareWallet) -> RustBuffer { - return FfiConverterTypeSupportedHardwareWallet.lower(value) +public func FfiConverterTypeTrezorPrevTxInput_lower(_ value: TrezorPrevTxInput) -> RustBuffer { + return FfiConverterTypeTrezorPrevTxInput.lower(value) } -public struct SweepResult { - /** - * The transaction ID of the sweep transaction - */ - public var txid: String - /** - * The total amount swept (in satoshis) - */ - public var amountSwept: UInt64 +/** + * Output of a previous transaction. + */ +public struct TrezorPrevTxOutput { /** - * The fee paid (in satoshis) + * Amount in satoshis */ - public var feePaid: UInt64 + public var amount: UInt64 /** - * The number of UTXOs swept + * Script pubkey (hex encoded) */ - public var utxosSwept: UInt32 + public var scriptPubkey: String // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * The transaction ID of the sweep transaction - */txid: String, - /** - * The total amount swept (in satoshis) - */amountSwept: UInt64, - /** - * The fee paid (in satoshis) - */feePaid: UInt64, + * Amount in satoshis + */amount: UInt64, /** - * The number of UTXOs swept - */utxosSwept: UInt32) { - self.txid = txid - self.amountSwept = amountSwept - self.feePaid = feePaid - self.utxosSwept = utxosSwept + * Script pubkey (hex encoded) + */scriptPubkey: String) { + self.amount = amount + self.scriptPubkey = scriptPubkey } } #if compiler(>=6) -extension SweepResult: Sendable {} +extension TrezorPrevTxOutput: Sendable {} #endif -extension SweepResult: Equatable, Hashable { - public static func ==(lhs: SweepResult, rhs: SweepResult) -> Bool { - if lhs.txid != rhs.txid { - return false - } - if lhs.amountSwept != rhs.amountSwept { - return false - } - if lhs.feePaid != rhs.feePaid { +extension TrezorPrevTxOutput: Equatable, Hashable { + public static func ==(lhs: TrezorPrevTxOutput, rhs: TrezorPrevTxOutput) -> Bool { + if lhs.amount != rhs.amount { return false } - if lhs.utxosSwept != rhs.utxosSwept { + if lhs.scriptPubkey != rhs.scriptPubkey { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(amountSwept) - hasher.combine(feePaid) - hasher.combine(utxosSwept) + hasher.combine(amount) + hasher.combine(scriptPubkey) } } -extension SweepResult: Codable {} +extension TrezorPrevTxOutput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSweepResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepResult { +public struct FfiConverterTypeTrezorPrevTxOutput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTxOutput { return - try SweepResult( - txid: FfiConverterString.read(from: &buf), - amountSwept: FfiConverterUInt64.read(from: &buf), - feePaid: FfiConverterUInt64.read(from: &buf), - utxosSwept: FfiConverterUInt32.read(from: &buf) + try TrezorPrevTxOutput( + amount: FfiConverterUInt64.read(from: &buf), + scriptPubkey: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: SweepResult, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt64.write(value.amountSwept, into: &buf) - FfiConverterUInt64.write(value.feePaid, into: &buf) - FfiConverterUInt32.write(value.utxosSwept, into: &buf) + public static func write(_ value: TrezorPrevTxOutput, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterString.write(value.scriptPubkey, into: &buf) } } @@ -11760,153 +14257,156 @@ public struct FfiConverterTypeSweepResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepResult_lift(_ buf: RustBuffer) throws -> SweepResult { - return try FfiConverterTypeSweepResult.lift(buf) +public func FfiConverterTypeTrezorPrevTxOutput_lift(_ buf: RustBuffer) throws -> TrezorPrevTxOutput { + return try FfiConverterTypeTrezorPrevTxOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepResult_lower(_ value: SweepResult) -> RustBuffer { - return FfiConverterTypeSweepResult.lower(value) +public func FfiConverterTypeTrezorPrevTxOutput_lower(_ value: TrezorPrevTxOutput) -> RustBuffer { + return FfiConverterTypeTrezorPrevTxOutput.lower(value) } -public struct SweepTransactionPreview { +/** + * Public key response from device. + */ +public struct TrezorPublicKeyResponse { /** - * The PSBT (Partially Signed Bitcoin Transaction) in base64 format + * Extended public key (xpub) */ - public var psbt: String + public var xpub: String /** - * The total amount available to sweep (in satoshis) + * The serialized path (e.g., "m/84'/0'/0'") */ - public var totalAmount: UInt64 + public var path: String /** - * The estimated fee for the transaction (in satoshis) + * Compressed public key (hex encoded) */ - public var estimatedFee: UInt64 + public var publicKey: String /** - * The estimated virtual size of the transaction (in vbytes) + * Chain code (hex encoded) */ - public var estimatedVsize: UInt64 + public var chainCode: String /** - * The number of UTXOs that will be swept + * Parent key fingerprint */ - public var utxosCount: UInt32 + public var fingerprint: UInt32 /** - * The destination address + * Derivation depth */ - public var destinationAddress: String + public var depth: UInt32 /** - * The amount that will be sent to destination after fees (in satoshis) + * Master root fingerprint (from the device's master seed) */ - public var amountAfterFees: UInt64 + public var rootFingerprint: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * The PSBT (Partially Signed Bitcoin Transaction) in base64 format - */psbt: String, + * Extended public key (xpub) + */xpub: String, /** - * The total amount available to sweep (in satoshis) - */totalAmount: UInt64, + * The serialized path (e.g., "m/84'/0'/0'") + */path: String, /** - * The estimated fee for the transaction (in satoshis) - */estimatedFee: UInt64, + * Compressed public key (hex encoded) + */publicKey: String, /** - * The estimated virtual size of the transaction (in vbytes) - */estimatedVsize: UInt64, + * Chain code (hex encoded) + */chainCode: String, /** - * The number of UTXOs that will be swept - */utxosCount: UInt32, + * Parent key fingerprint + */fingerprint: UInt32, /** - * The destination address - */destinationAddress: String, + * Derivation depth + */depth: UInt32, /** - * The amount that will be sent to destination after fees (in satoshis) - */amountAfterFees: UInt64) { - self.psbt = psbt - self.totalAmount = totalAmount - self.estimatedFee = estimatedFee - self.estimatedVsize = estimatedVsize - self.utxosCount = utxosCount - self.destinationAddress = destinationAddress - self.amountAfterFees = amountAfterFees + * Master root fingerprint (from the device's master seed) + */rootFingerprint: UInt32?) { + self.xpub = xpub + self.path = path + self.publicKey = publicKey + self.chainCode = chainCode + self.fingerprint = fingerprint + self.depth = depth + self.rootFingerprint = rootFingerprint } } #if compiler(>=6) -extension SweepTransactionPreview: Sendable {} +extension TrezorPublicKeyResponse: Sendable {} #endif -extension SweepTransactionPreview: Equatable, Hashable { - public static func ==(lhs: SweepTransactionPreview, rhs: SweepTransactionPreview) -> Bool { - if lhs.psbt != rhs.psbt { +extension TrezorPublicKeyResponse: Equatable, Hashable { + public static func ==(lhs: TrezorPublicKeyResponse, rhs: TrezorPublicKeyResponse) -> Bool { + if lhs.xpub != rhs.xpub { return false } - if lhs.totalAmount != rhs.totalAmount { + if lhs.path != rhs.path { return false } - if lhs.estimatedFee != rhs.estimatedFee { + if lhs.publicKey != rhs.publicKey { return false } - if lhs.estimatedVsize != rhs.estimatedVsize { + if lhs.chainCode != rhs.chainCode { return false } - if lhs.utxosCount != rhs.utxosCount { + if lhs.fingerprint != rhs.fingerprint { return false } - if lhs.destinationAddress != rhs.destinationAddress { + if lhs.depth != rhs.depth { return false } - if lhs.amountAfterFees != rhs.amountAfterFees { + if lhs.rootFingerprint != rhs.rootFingerprint { return false } return true } - public func hash(into hasher: inout Hasher) { - hasher.combine(psbt) - hasher.combine(totalAmount) - hasher.combine(estimatedFee) - hasher.combine(estimatedVsize) - hasher.combine(utxosCount) - hasher.combine(destinationAddress) - hasher.combine(amountAfterFees) + public func hash(into hasher: inout Hasher) { + hasher.combine(xpub) + hasher.combine(path) + hasher.combine(publicKey) + hasher.combine(chainCode) + hasher.combine(fingerprint) + hasher.combine(depth) + hasher.combine(rootFingerprint) } } -extension SweepTransactionPreview: Codable {} +extension TrezorPublicKeyResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSweepTransactionPreview: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepTransactionPreview { +public struct FfiConverterTypeTrezorPublicKeyResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPublicKeyResponse { return - try SweepTransactionPreview( - psbt: FfiConverterString.read(from: &buf), - totalAmount: FfiConverterUInt64.read(from: &buf), - estimatedFee: FfiConverterUInt64.read(from: &buf), - estimatedVsize: FfiConverterUInt64.read(from: &buf), - utxosCount: FfiConverterUInt32.read(from: &buf), - destinationAddress: FfiConverterString.read(from: &buf), - amountAfterFees: FfiConverterUInt64.read(from: &buf) + try TrezorPublicKeyResponse( + xpub: FfiConverterString.read(from: &buf), + path: FfiConverterString.read(from: &buf), + publicKey: FfiConverterString.read(from: &buf), + chainCode: FfiConverterString.read(from: &buf), + fingerprint: FfiConverterUInt32.read(from: &buf), + depth: FfiConverterUInt32.read(from: &buf), + rootFingerprint: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: SweepTransactionPreview, into buf: inout [UInt8]) { - FfiConverterString.write(value.psbt, into: &buf) - FfiConverterUInt64.write(value.totalAmount, into: &buf) - FfiConverterUInt64.write(value.estimatedFee, into: &buf) - FfiConverterUInt64.write(value.estimatedVsize, into: &buf) - FfiConverterUInt32.write(value.utxosCount, into: &buf) - FfiConverterString.write(value.destinationAddress, into: &buf) - FfiConverterUInt64.write(value.amountAfterFees, into: &buf) + public static func write(_ value: TrezorPublicKeyResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.xpub, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterString.write(value.publicKey, into: &buf) + FfiConverterString.write(value.chainCode, into: &buf) + FfiConverterUInt32.write(value.fingerprint, into: &buf) + FfiConverterUInt32.write(value.depth, into: &buf) + FfiConverterOptionUInt32.write(value.rootFingerprint, into: &buf) } } @@ -11914,167 +14414,100 @@ public struct FfiConverterTypeSweepTransactionPreview: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepTransactionPreview_lift(_ buf: RustBuffer) throws -> SweepTransactionPreview { - return try FfiConverterTypeSweepTransactionPreview.lift(buf) +public func FfiConverterTypeTrezorPublicKeyResponse_lift(_ buf: RustBuffer) throws -> TrezorPublicKeyResponse { + return try FfiConverterTypeTrezorPublicKeyResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepTransactionPreview_lower(_ value: SweepTransactionPreview) -> RustBuffer { - return FfiConverterTypeSweepTransactionPreview.lower(value) +public func FfiConverterTypeTrezorPublicKeyResponse_lower(_ value: TrezorPublicKeyResponse) -> RustBuffer { + return FfiConverterTypeTrezorPublicKeyResponse.lower(value) } -public struct SweepableBalances { - /** - * Balance in legacy (P2PKH) addresses (in satoshis) - */ - public var legacyBalance: UInt64 - /** - * Balance in P2SH-SegWit (P2SH-P2WPKH) addresses (in satoshis) - */ - public var p2shBalance: UInt64 - /** - * Balance in Taproot (P2TR) addresses (in satoshis) - */ - public var taprootBalance: UInt64 - /** - * Total balance across all wallet types (in satoshis) - */ - public var totalBalance: UInt64 - /** - * Number of UTXOs in legacy wallet - */ - public var legacyUtxosCount: UInt32 +/** + * Parameters for signing a message. + */ +public struct TrezorSignMessageParams { /** - * Number of UTXOs in P2SH-SegWit wallet + * BIP32 path for the signing key (e.g., "m/84'/0'/0'/0/0") */ - public var p2shUtxosCount: UInt32 + public var path: String /** - * Number of UTXOs in Taproot wallet + * Message to sign */ - public var taprootUtxosCount: UInt32 + public var message: String /** - * Total number of UTXOs across all wallet types + * Coin network (default: Bitcoin) */ - public var totalUtxosCount: UInt32 + public var coin: TrezorCoinType? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Balance in legacy (P2PKH) addresses (in satoshis) - */legacyBalance: UInt64, - /** - * Balance in P2SH-SegWit (P2SH-P2WPKH) addresses (in satoshis) - */p2shBalance: UInt64, - /** - * Balance in Taproot (P2TR) addresses (in satoshis) - */taprootBalance: UInt64, - /** - * Total balance across all wallet types (in satoshis) - */totalBalance: UInt64, - /** - * Number of UTXOs in legacy wallet - */legacyUtxosCount: UInt32, - /** - * Number of UTXOs in P2SH-SegWit wallet - */p2shUtxosCount: UInt32, + * BIP32 path for the signing key (e.g., "m/84'/0'/0'/0/0") + */path: String, /** - * Number of UTXOs in Taproot wallet - */taprootUtxosCount: UInt32, + * Message to sign + */message: String, /** - * Total number of UTXOs across all wallet types - */totalUtxosCount: UInt32) { - self.legacyBalance = legacyBalance - self.p2shBalance = p2shBalance - self.taprootBalance = taprootBalance - self.totalBalance = totalBalance - self.legacyUtxosCount = legacyUtxosCount - self.p2shUtxosCount = p2shUtxosCount - self.taprootUtxosCount = taprootUtxosCount - self.totalUtxosCount = totalUtxosCount + * Coin network (default: Bitcoin) + */coin: TrezorCoinType?) { + self.path = path + self.message = message + self.coin = coin } } #if compiler(>=6) -extension SweepableBalances: Sendable {} +extension TrezorSignMessageParams: Sendable {} #endif -extension SweepableBalances: Equatable, Hashable { - public static func ==(lhs: SweepableBalances, rhs: SweepableBalances) -> Bool { - if lhs.legacyBalance != rhs.legacyBalance { - return false - } - if lhs.p2shBalance != rhs.p2shBalance { - return false - } - if lhs.taprootBalance != rhs.taprootBalance { - return false - } - if lhs.totalBalance != rhs.totalBalance { - return false - } - if lhs.legacyUtxosCount != rhs.legacyUtxosCount { - return false - } - if lhs.p2shUtxosCount != rhs.p2shUtxosCount { +extension TrezorSignMessageParams: Equatable, Hashable { + public static func ==(lhs: TrezorSignMessageParams, rhs: TrezorSignMessageParams) -> Bool { + if lhs.path != rhs.path { return false } - if lhs.taprootUtxosCount != rhs.taprootUtxosCount { + if lhs.message != rhs.message { return false } - if lhs.totalUtxosCount != rhs.totalUtxosCount { + if lhs.coin != rhs.coin { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(legacyBalance) - hasher.combine(p2shBalance) - hasher.combine(taprootBalance) - hasher.combine(totalBalance) - hasher.combine(legacyUtxosCount) - hasher.combine(p2shUtxosCount) - hasher.combine(taprootUtxosCount) - hasher.combine(totalUtxosCount) + hasher.combine(path) + hasher.combine(message) + hasher.combine(coin) } } -extension SweepableBalances: Codable {} +extension TrezorSignMessageParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSweepableBalances: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepableBalances { +public struct FfiConverterTypeTrezorSignMessageParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignMessageParams { return - try SweepableBalances( - legacyBalance: FfiConverterUInt64.read(from: &buf), - p2shBalance: FfiConverterUInt64.read(from: &buf), - taprootBalance: FfiConverterUInt64.read(from: &buf), - totalBalance: FfiConverterUInt64.read(from: &buf), - legacyUtxosCount: FfiConverterUInt32.read(from: &buf), - p2shUtxosCount: FfiConverterUInt32.read(from: &buf), - taprootUtxosCount: FfiConverterUInt32.read(from: &buf), - totalUtxosCount: FfiConverterUInt32.read(from: &buf) + try TrezorSignMessageParams( + path: FfiConverterString.read(from: &buf), + message: FfiConverterString.read(from: &buf), + coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf) ) } - public static func write(_ value: SweepableBalances, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.legacyBalance, into: &buf) - FfiConverterUInt64.write(value.p2shBalance, into: &buf) - FfiConverterUInt64.write(value.taprootBalance, into: &buf) - FfiConverterUInt64.write(value.totalBalance, into: &buf) - FfiConverterUInt32.write(value.legacyUtxosCount, into: &buf) - FfiConverterUInt32.write(value.p2shUtxosCount, into: &buf) - FfiConverterUInt32.write(value.taprootUtxosCount, into: &buf) - FfiConverterUInt32.write(value.totalUtxosCount, into: &buf) + public static func write(_ value: TrezorSignMessageParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterString.write(value.message, into: &buf) + FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) } } @@ -12082,282 +14515,229 @@ public struct FfiConverterTypeSweepableBalances: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepableBalances_lift(_ buf: RustBuffer) throws -> SweepableBalances { - return try FfiConverterTypeSweepableBalances.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeSweepableBalances_lower(_ value: SweepableBalances) -> RustBuffer { - return FfiConverterTypeSweepableBalances.lower(value) -} - - -/** - * Full details for a single transaction, including raw inputs/outputs and size metrics. - */ -public struct TransactionDetail { - /** - * Transaction ID (hex) - */ - public var txid: String - /** - * Amount received by the wallet (sats) - */ - public var received: UInt64 - /** - * Amount sent by the wallet (sats) — includes change sent back to self - */ - public var sent: UInt64 - /** - * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) - */ - public var net: Int64 - /** - * Display amount in sats (same semantics as HistoryTransaction.amount) - */ - public var amount: UInt64 - /** - * Transaction fee in sats (None if not available) - */ - public var fee: UInt64? - /** - * Transaction direction - */ - public var direction: TxDirection - /** - * Block height (None if unconfirmed/mempool) - */ - public var blockHeight: UInt32? - /** - * Block timestamp as unix epoch seconds (None if unconfirmed) - */ - public var timestamp: UInt64? - /** - * Number of confirmations (0 if unconfirmed) - */ - public var confirmations: UInt32 +public func FfiConverterTypeTrezorSignMessageParams_lift(_ buf: RustBuffer) throws -> TrezorSignMessageParams { + return try FfiConverterTypeTrezorSignMessageParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTrezorSignMessageParams_lower(_ value: TrezorSignMessageParams) -> RustBuffer { + return FfiConverterTypeTrezorSignMessageParams.lower(value) +} + + +/** + * Parameters for signing a transaction. + */ +public struct TrezorSignTxParams { /** * Transaction inputs */ - public var inputs: [TxDetailInput] + public var inputs: [TrezorTxInput] /** * Transaction outputs */ - public var outputs: [TxDetailOutput] + public var outputs: [TrezorTxOutput] /** - * Serialized transaction size in bytes + * Coin network (default: Bitcoin) */ - public var size: UInt32 + public var coin: TrezorCoinType? /** - * Virtual size in vbytes (ceil(weight / 4)) + * Lock time (default: 0) */ - public var vsize: UInt32 + public var lockTime: UInt32? /** - * Transaction weight in weight units + * Version (default: 2) */ - public var weight: UInt32 + public var version: UInt32? /** - * Fee rate in sat/vB (fee / vsize), None if fee is unavailable or vsize is zero + * Previous transactions (for non-SegWit input verification) */ - public var feeRate: Double? + public var prevTxs: [TrezorPrevTx] // Default memberwise initializers are never public by default, so we // declare one manually. public init( - /** - * Transaction ID (hex) - */txid: String, - /** - * Amount received by the wallet (sats) - */received: UInt64, - /** - * Amount sent by the wallet (sats) — includes change sent back to self - */sent: UInt64, - /** - * Net value from wallet's perspective: received - sent (positive = inflow, negative = outflow) - */net: Int64, - /** - * Display amount in sats (same semantics as HistoryTransaction.amount) - */amount: UInt64, - /** - * Transaction fee in sats (None if not available) - */fee: UInt64?, - /** - * Transaction direction - */direction: TxDirection, - /** - * Block height (None if unconfirmed/mempool) - */blockHeight: UInt32?, - /** - * Block timestamp as unix epoch seconds (None if unconfirmed) - */timestamp: UInt64?, - /** - * Number of confirmations (0 if unconfirmed) - */confirmations: UInt32, /** * Transaction inputs - */inputs: [TxDetailInput], + */inputs: [TrezorTxInput], /** * Transaction outputs - */outputs: [TxDetailOutput], + */outputs: [TrezorTxOutput], /** - * Serialized transaction size in bytes - */size: UInt32, + * Coin network (default: Bitcoin) + */coin: TrezorCoinType?, /** - * Virtual size in vbytes (ceil(weight / 4)) - */vsize: UInt32, + * Lock time (default: 0) + */lockTime: UInt32?, /** - * Transaction weight in weight units - */weight: UInt32, + * Version (default: 2) + */version: UInt32?, /** - * Fee rate in sat/vB (fee / vsize), None if fee is unavailable or vsize is zero - */feeRate: Double?) { - self.txid = txid - self.received = received - self.sent = sent - self.net = net - self.amount = amount - self.fee = fee - self.direction = direction - self.blockHeight = blockHeight - self.timestamp = timestamp - self.confirmations = confirmations + * Previous transactions (for non-SegWit input verification) + */prevTxs: [TrezorPrevTx]) { self.inputs = inputs self.outputs = outputs - self.size = size - self.vsize = vsize - self.weight = weight - self.feeRate = feeRate + self.coin = coin + self.lockTime = lockTime + self.version = version + self.prevTxs = prevTxs } } #if compiler(>=6) -extension TransactionDetail: Sendable {} +extension TrezorSignTxParams: Sendable {} #endif -extension TransactionDetail: Equatable, Hashable { - public static func ==(lhs: TransactionDetail, rhs: TransactionDetail) -> Bool { - if lhs.txid != rhs.txid { - return false - } - if lhs.received != rhs.received { - return false - } - if lhs.sent != rhs.sent { - return false - } - if lhs.net != rhs.net { - return false - } - if lhs.amount != rhs.amount { - return false - } - if lhs.fee != rhs.fee { - return false - } - if lhs.direction != rhs.direction { - return false - } - if lhs.blockHeight != rhs.blockHeight { - return false - } - if lhs.timestamp != rhs.timestamp { - return false - } - if lhs.confirmations != rhs.confirmations { - return false - } +extension TrezorSignTxParams: Equatable, Hashable { + public static func ==(lhs: TrezorSignTxParams, rhs: TrezorSignTxParams) -> Bool { if lhs.inputs != rhs.inputs { return false } if lhs.outputs != rhs.outputs { return false } - if lhs.size != rhs.size { + if lhs.coin != rhs.coin { return false } - if lhs.vsize != rhs.vsize { + if lhs.lockTime != rhs.lockTime { return false } - if lhs.weight != rhs.weight { + if lhs.version != rhs.version { return false } - if lhs.feeRate != rhs.feeRate { + if lhs.prevTxs != rhs.prevTxs { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(received) - hasher.combine(sent) - hasher.combine(net) - hasher.combine(amount) - hasher.combine(fee) - hasher.combine(direction) - hasher.combine(blockHeight) - hasher.combine(timestamp) - hasher.combine(confirmations) hasher.combine(inputs) hasher.combine(outputs) - hasher.combine(size) - hasher.combine(vsize) - hasher.combine(weight) - hasher.combine(feeRate) + hasher.combine(coin) + hasher.combine(lockTime) + hasher.combine(version) + hasher.combine(prevTxs) + } +} + +extension TrezorSignTxParams: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTrezorSignTxParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignTxParams { + return + try TrezorSignTxParams( + inputs: FfiConverterSequenceTypeTrezorTxInput.read(from: &buf), + outputs: FfiConverterSequenceTypeTrezorTxOutput.read(from: &buf), + coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), + lockTime: FfiConverterOptionUInt32.read(from: &buf), + version: FfiConverterOptionUInt32.read(from: &buf), + prevTxs: FfiConverterSequenceTypeTrezorPrevTx.read(from: &buf) + ) + } + + public static func write(_ value: TrezorSignTxParams, into buf: inout [UInt8]) { + FfiConverterSequenceTypeTrezorTxInput.write(value.inputs, into: &buf) + FfiConverterSequenceTypeTrezorTxOutput.write(value.outputs, into: &buf) + FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) + FfiConverterOptionUInt32.write(value.lockTime, into: &buf) + FfiConverterOptionUInt32.write(value.version, into: &buf) + FfiConverterSequenceTypeTrezorPrevTx.write(value.prevTxs, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTrezorSignTxParams_lift(_ buf: RustBuffer) throws -> TrezorSignTxParams { + return try FfiConverterTypeTrezorSignTxParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTrezorSignTxParams_lower(_ value: TrezorSignTxParams) -> RustBuffer { + return FfiConverterTypeTrezorSignTxParams.lower(value) +} + + +/** + * Response from signing a message. + */ +public struct TrezorSignedMessageResponse { + /** + * Bitcoin address that signed the message + */ + public var address: String + /** + * Signature (base64 encoded) + */ + public var signature: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Bitcoin address that signed the message + */address: String, + /** + * Signature (base64 encoded) + */signature: String) { + self.address = address + self.signature = signature + } +} + +#if compiler(>=6) +extension TrezorSignedMessageResponse: Sendable {} +#endif + + +extension TrezorSignedMessageResponse: Equatable, Hashable { + public static func ==(lhs: TrezorSignedMessageResponse, rhs: TrezorSignedMessageResponse) -> Bool { + if lhs.address != rhs.address { + return false + } + if lhs.signature != rhs.signature { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(address) + hasher.combine(signature) } } -extension TransactionDetail: Codable {} +extension TrezorSignedMessageResponse: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTransactionDetail: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionDetail { +public struct FfiConverterTypeTrezorSignedMessageResponse: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignedMessageResponse { return - try TransactionDetail( - txid: FfiConverterString.read(from: &buf), - received: FfiConverterUInt64.read(from: &buf), - sent: FfiConverterUInt64.read(from: &buf), - net: FfiConverterInt64.read(from: &buf), - amount: FfiConverterUInt64.read(from: &buf), - fee: FfiConverterOptionUInt64.read(from: &buf), - direction: FfiConverterTypeTxDirection.read(from: &buf), - blockHeight: FfiConverterOptionUInt32.read(from: &buf), - timestamp: FfiConverterOptionUInt64.read(from: &buf), - confirmations: FfiConverterUInt32.read(from: &buf), - inputs: FfiConverterSequenceTypeTxDetailInput.read(from: &buf), - outputs: FfiConverterSequenceTypeTxDetailOutput.read(from: &buf), - size: FfiConverterUInt32.read(from: &buf), - vsize: FfiConverterUInt32.read(from: &buf), - weight: FfiConverterUInt32.read(from: &buf), - feeRate: FfiConverterOptionDouble.read(from: &buf) + try TrezorSignedMessageResponse( + address: FfiConverterString.read(from: &buf), + signature: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: TransactionDetail, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt64.write(value.received, into: &buf) - FfiConverterUInt64.write(value.sent, into: &buf) - FfiConverterInt64.write(value.net, into: &buf) - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterOptionUInt64.write(value.fee, into: &buf) - FfiConverterTypeTxDirection.write(value.direction, into: &buf) - FfiConverterOptionUInt32.write(value.blockHeight, into: &buf) - FfiConverterOptionUInt64.write(value.timestamp, into: &buf) - FfiConverterUInt32.write(value.confirmations, into: &buf) - FfiConverterSequenceTypeTxDetailInput.write(value.inputs, into: &buf) - FfiConverterSequenceTypeTxDetailOutput.write(value.outputs, into: &buf) - FfiConverterUInt32.write(value.size, into: &buf) - FfiConverterUInt32.write(value.vsize, into: &buf) - FfiConverterUInt32.write(value.weight, into: &buf) - FfiConverterOptionDouble.write(value.feeRate, into: &buf) + public static func write(_ value: TrezorSignedMessageResponse, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.signature, into: &buf) } } @@ -12365,132 +14745,100 @@ public struct FfiConverterTypeTransactionDetail: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionDetail_lift(_ buf: RustBuffer) throws -> TransactionDetail { - return try FfiConverterTypeTransactionDetail.lift(buf) +public func FfiConverterTypeTrezorSignedMessageResponse_lift(_ buf: RustBuffer) throws -> TrezorSignedMessageResponse { + return try FfiConverterTypeTrezorSignedMessageResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionDetail_lower(_ value: TransactionDetail) -> RustBuffer { - return FfiConverterTypeTransactionDetail.lower(value) +public func FfiConverterTypeTrezorSignedMessageResponse_lower(_ value: TrezorSignedMessageResponse) -> RustBuffer { + return FfiConverterTypeTrezorSignedMessageResponse.lower(value) } /** - * Details about an onchain transaction. + * Signed transaction result. */ -public struct TransactionDetails { - public var walletId: String - /** - * The transaction ID. - */ - public var txId: String +public struct TrezorSignedTx { /** - * The net amount in this transaction (in satoshis). - * - * This is calculated as: (received - sent). For incoming payments, - * this will be positive. For outgoing payments, this will be negative. - * - * Note: This amount does NOT include transaction fees. + * Signatures for each input (hex encoded) */ - public var amountSats: Int64 + public var signatures: [String] /** - * The transaction inputs with full details. + * Serialized transaction (hex) */ - public var inputs: [TxInput] + public var serializedTx: String /** - * The transaction outputs with full details. + * Broadcast transaction ID (populated when push=true) */ - public var outputs: [TxOutput] + public var txid: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(walletId: String, - /** - * The transaction ID. - */txId: String, + public init( /** - * The net amount in this transaction (in satoshis). - * - * This is calculated as: (received - sent). For incoming payments, - * this will be positive. For outgoing payments, this will be negative. - * - * Note: This amount does NOT include transaction fees. - */amountSats: Int64, + * Signatures for each input (hex encoded) + */signatures: [String], /** - * The transaction inputs with full details. - */inputs: [TxInput], + * Serialized transaction (hex) + */serializedTx: String, /** - * The transaction outputs with full details. - */outputs: [TxOutput]) { - self.walletId = walletId - self.txId = txId - self.amountSats = amountSats - self.inputs = inputs - self.outputs = outputs + * Broadcast transaction ID (populated when push=true) + */txid: String?) { + self.signatures = signatures + self.serializedTx = serializedTx + self.txid = txid } } #if compiler(>=6) -extension TransactionDetails: Sendable {} +extension TrezorSignedTx: Sendable {} #endif -extension TransactionDetails: Equatable, Hashable { - public static func ==(lhs: TransactionDetails, rhs: TransactionDetails) -> Bool { - if lhs.walletId != rhs.walletId { - return false - } - if lhs.txId != rhs.txId { - return false - } - if lhs.amountSats != rhs.amountSats { +extension TrezorSignedTx: Equatable, Hashable { + public static func ==(lhs: TrezorSignedTx, rhs: TrezorSignedTx) -> Bool { + if lhs.signatures != rhs.signatures { return false } - if lhs.inputs != rhs.inputs { + if lhs.serializedTx != rhs.serializedTx { return false } - if lhs.outputs != rhs.outputs { + if lhs.txid != rhs.txid { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(walletId) - hasher.combine(txId) - hasher.combine(amountSats) - hasher.combine(inputs) - hasher.combine(outputs) + hasher.combine(signatures) + hasher.combine(serializedTx) + hasher.combine(txid) } } -extension TransactionDetails: Codable {} +extension TrezorSignedTx: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTransactionDetails: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionDetails { +public struct FfiConverterTypeTrezorSignedTx: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignedTx { return - try TransactionDetails( - walletId: FfiConverterString.read(from: &buf), - txId: FfiConverterString.read(from: &buf), - amountSats: FfiConverterInt64.read(from: &buf), - inputs: FfiConverterSequenceTypeTxInput.read(from: &buf), - outputs: FfiConverterSequenceTypeTxOutput.read(from: &buf) + try TrezorSignedTx( + signatures: FfiConverterSequenceString.read(from: &buf), + serializedTx: FfiConverterString.read(from: &buf), + txid: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: TransactionDetails, into buf: inout [UInt8]) { - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.txId, into: &buf) - FfiConverterInt64.write(value.amountSats, into: &buf) - FfiConverterSequenceTypeTxInput.write(value.inputs, into: &buf) - FfiConverterSequenceTypeTxOutput.write(value.outputs, into: &buf) + public static func write(_ value: TrezorSignedTx, into buf: inout [UInt8]) { + FfiConverterSequenceString.write(value.signatures, into: &buf) + FfiConverterString.write(value.serializedTx, into: &buf) + FfiConverterOptionString.write(value.txid, into: &buf) } } @@ -12498,128 +14846,114 @@ public struct FfiConverterTypeTransactionDetails: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionDetails_lift(_ buf: RustBuffer) throws -> TransactionDetails { - return try FfiConverterTypeTransactionDetails.lift(buf) +public func FfiConverterTypeTrezorSignedTx_lift(_ buf: RustBuffer) throws -> TrezorSignedTx { + return try FfiConverterTypeTrezorSignedTx.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionDetails_lower(_ value: TransactionDetails) -> RustBuffer { - return FfiConverterTypeTransactionDetails.lower(value) +public func FfiConverterTypeTrezorSignedTx_lower(_ value: TrezorSignedTx) -> RustBuffer { + return FfiConverterTypeTrezorSignedTx.lower(value) } /** - * Result from querying transaction history for an xpub. + * Result from a transport read operation */ -public struct TransactionHistoryResult { - /** - * All transactions, sorted: unconfirmed first, then by timestamp descending - */ - public var transactions: [HistoryTransaction] +public struct TrezorTransportReadResult { /** - * Balance breakdown + * Whether the read succeeded */ - public var balance: WalletBalance + public var success: Bool /** - * Total number of transactions + * Data read (empty on failure) */ - public var txCount: UInt32 + public var data: Data /** - * Current blockchain tip height + * Error message (empty on success) */ - public var blockHeight: UInt32 + public var error: String /** - * The detected or specified account type + * Structured error code (None on success or when the native error is generic) */ - public var accountType: AccountType + public var errorCode: TrezorTransportErrorCode? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * All transactions, sorted: unconfirmed first, then by timestamp descending - */transactions: [HistoryTransaction], - /** - * Balance breakdown - */balance: WalletBalance, + * Whether the read succeeded + */success: Bool, /** - * Total number of transactions - */txCount: UInt32, + * Data read (empty on failure) + */data: Data, /** - * Current blockchain tip height - */blockHeight: UInt32, + * Error message (empty on success) + */error: String, /** - * The detected or specified account type - */accountType: AccountType) { - self.transactions = transactions - self.balance = balance - self.txCount = txCount - self.blockHeight = blockHeight - self.accountType = accountType + * Structured error code (None on success or when the native error is generic) + */errorCode: TrezorTransportErrorCode?) { + self.success = success + self.data = data + self.error = error + self.errorCode = errorCode } } #if compiler(>=6) -extension TransactionHistoryResult: Sendable {} +extension TrezorTransportReadResult: Sendable {} #endif -extension TransactionHistoryResult: Equatable, Hashable { - public static func ==(lhs: TransactionHistoryResult, rhs: TransactionHistoryResult) -> Bool { - if lhs.transactions != rhs.transactions { - return false - } - if lhs.balance != rhs.balance { +extension TrezorTransportReadResult: Equatable, Hashable { + public static func ==(lhs: TrezorTransportReadResult, rhs: TrezorTransportReadResult) -> Bool { + if lhs.success != rhs.success { return false } - if lhs.txCount != rhs.txCount { + if lhs.data != rhs.data { return false } - if lhs.blockHeight != rhs.blockHeight { + if lhs.error != rhs.error { return false } - if lhs.accountType != rhs.accountType { + if lhs.errorCode != rhs.errorCode { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(transactions) - hasher.combine(balance) - hasher.combine(txCount) - hasher.combine(blockHeight) - hasher.combine(accountType) + hasher.combine(success) + hasher.combine(data) + hasher.combine(error) + hasher.combine(errorCode) } } -extension TransactionHistoryResult: Codable {} +extension TrezorTransportReadResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTransactionHistoryResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TransactionHistoryResult { +public struct FfiConverterTypeTrezorTransportReadResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportReadResult { return - try TransactionHistoryResult( - transactions: FfiConverterSequenceTypeHistoryTransaction.read(from: &buf), - balance: FfiConverterTypeWalletBalance.read(from: &buf), - txCount: FfiConverterUInt32.read(from: &buf), - blockHeight: FfiConverterUInt32.read(from: &buf), - accountType: FfiConverterTypeAccountType.read(from: &buf) + try TrezorTransportReadResult( + success: FfiConverterBool.read(from: &buf), + data: FfiConverterData.read(from: &buf), + error: FfiConverterString.read(from: &buf), + errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } - public static func write(_ value: TransactionHistoryResult, into buf: inout [UInt8]) { - FfiConverterSequenceTypeHistoryTransaction.write(value.transactions, into: &buf) - FfiConverterTypeWalletBalance.write(value.balance, into: &buf) - FfiConverterUInt32.write(value.txCount, into: &buf) - FfiConverterUInt32.write(value.blockHeight, into: &buf) - FfiConverterTypeAccountType.write(value.accountType, into: &buf) + public static func write(_ value: TrezorTransportReadResult, into buf: inout [UInt8]) { + FfiConverterBool.write(value.success, into: &buf) + FfiConverterData.write(value.data, into: &buf) + FfiConverterString.write(value.error, into: &buf) + FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) } } @@ -12627,86 +14961,100 @@ public struct FfiConverterTypeTransactionHistoryResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionHistoryResult_lift(_ buf: RustBuffer) throws -> TransactionHistoryResult { - return try FfiConverterTypeTransactionHistoryResult.lift(buf) +public func FfiConverterTypeTrezorTransportReadResult_lift(_ buf: RustBuffer) throws -> TrezorTransportReadResult { + return try FfiConverterTypeTrezorTransportReadResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTransactionHistoryResult_lower(_ value: TransactionHistoryResult) -> RustBuffer { - return FfiConverterTypeTransactionHistoryResult.lower(value) +public func FfiConverterTypeTrezorTransportReadResult_lower(_ value: TrezorTransportReadResult) -> RustBuffer { + return FfiConverterTypeTrezorTransportReadResult.lower(value) } /** - * Address response from device. + * Result from a transport write or open operation */ -public struct TrezorAddressResponse { +public struct TrezorTransportWriteResult { /** - * The Bitcoin address + * Whether the operation succeeded */ - public var address: String + public var success: Bool /** - * The serialized path (e.g., "m/84'/0'/0'/0/0") + * Error message (empty on success) */ - public var path: String + public var error: String + /** + * Structured error code (None on success or when the native error is generic) + */ + public var errorCode: TrezorTransportErrorCode? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * The Bitcoin address - */address: String, + * Whether the operation succeeded + */success: Bool, /** - * The serialized path (e.g., "m/84'/0'/0'/0/0") - */path: String) { - self.address = address - self.path = path + * Error message (empty on success) + */error: String, + /** + * Structured error code (None on success or when the native error is generic) + */errorCode: TrezorTransportErrorCode?) { + self.success = success + self.error = error + self.errorCode = errorCode } } #if compiler(>=6) -extension TrezorAddressResponse: Sendable {} +extension TrezorTransportWriteResult: Sendable {} #endif -extension TrezorAddressResponse: Equatable, Hashable { - public static func ==(lhs: TrezorAddressResponse, rhs: TrezorAddressResponse) -> Bool { - if lhs.address != rhs.address { +extension TrezorTransportWriteResult: Equatable, Hashable { + public static func ==(lhs: TrezorTransportWriteResult, rhs: TrezorTransportWriteResult) -> Bool { + if lhs.success != rhs.success { return false } - if lhs.path != rhs.path { + if lhs.error != rhs.error { + return false + } + if lhs.errorCode != rhs.errorCode { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(path) + hasher.combine(success) + hasher.combine(error) + hasher.combine(errorCode) } } -extension TrezorAddressResponse: Codable {} +extension TrezorTransportWriteResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorAddressResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorAddressResponse { +public struct FfiConverterTypeTrezorTransportWriteResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportWriteResult { return - try TrezorAddressResponse( - address: FfiConverterString.read(from: &buf), - path: FfiConverterString.read(from: &buf) + try TrezorTransportWriteResult( + success: FfiConverterBool.read(from: &buf), + error: FfiConverterString.read(from: &buf), + errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) ) } - public static func write(_ value: TrezorAddressResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.path, into: &buf) + public static func write(_ value: TrezorTransportWriteResult, into buf: inout [UInt8]) { + FfiConverterBool.write(value.success, into: &buf) + FfiConverterString.write(value.error, into: &buf) + FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) } } @@ -12714,128 +15062,170 @@ public struct FfiConverterTypeTrezorAddressResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorAddressResponse_lift(_ buf: RustBuffer) throws -> TrezorAddressResponse { - return try FfiConverterTypeTrezorAddressResponse.lift(buf) +public func FfiConverterTypeTrezorTransportWriteResult_lift(_ buf: RustBuffer) throws -> TrezorTransportWriteResult { + return try FfiConverterTypeTrezorTransportWriteResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorAddressResponse_lower(_ value: TrezorAddressResponse) -> RustBuffer { - return FfiConverterTypeTrezorAddressResponse.lower(value) +public func FfiConverterTypeTrezorTransportWriteResult_lower(_ value: TrezorTransportWriteResult) -> RustBuffer { + return FfiConverterTypeTrezorTransportWriteResult.lower(value) } /** - * Result from a high-level message call (for BLE/THP devices) + * Transaction input for signing. */ -public struct TrezorCallMessageResult { +public struct TrezorTxInput { /** - * Whether the call succeeded + * Previous transaction hash (hex, 32 bytes) */ - public var success: Bool + public var prevHash: String /** - * Response message type + * Previous output index */ - public var messageType: UInt16 + public var prevIndex: UInt32 /** - * Response protobuf data + * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") */ - public var data: Data + public var path: String /** - * Error message (empty on success) + * Amount in satoshis */ - public var error: String + public var amount: UInt64 /** - * Structured error code (None on success or when the native error is generic) + * Script type */ - public var errorCode: TrezorTransportErrorCode? + public var scriptType: TrezorScriptType + /** + * Sequence number (default: 0xFFFFFFFD for RBF) + */ + public var sequence: UInt32? + /** + * Original transaction hash for RBF replacement (hex encoded) + */ + public var origHash: String? + /** + * Original input index for RBF replacement + */ + public var origIndex: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Whether the call succeeded - */success: Bool, + * Previous transaction hash (hex, 32 bytes) + */prevHash: String, /** - * Response message type - */messageType: UInt16, + * Previous output index + */prevIndex: UInt32, /** - * Response protobuf data - */data: Data, + * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") + */path: String, /** - * Error message (empty on success) - */error: String, + * Amount in satoshis + */amount: UInt64, /** - * Structured error code (None on success or when the native error is generic) - */errorCode: TrezorTransportErrorCode?) { - self.success = success - self.messageType = messageType - self.data = data - self.error = error - self.errorCode = errorCode + * Script type + */scriptType: TrezorScriptType, + /** + * Sequence number (default: 0xFFFFFFFD for RBF) + */sequence: UInt32?, + /** + * Original transaction hash for RBF replacement (hex encoded) + */origHash: String?, + /** + * Original input index for RBF replacement + */origIndex: UInt32?) { + self.prevHash = prevHash + self.prevIndex = prevIndex + self.path = path + self.amount = amount + self.scriptType = scriptType + self.sequence = sequence + self.origHash = origHash + self.origIndex = origIndex } } #if compiler(>=6) -extension TrezorCallMessageResult: Sendable {} +extension TrezorTxInput: Sendable {} #endif -extension TrezorCallMessageResult: Equatable, Hashable { - public static func ==(lhs: TrezorCallMessageResult, rhs: TrezorCallMessageResult) -> Bool { - if lhs.success != rhs.success { +extension TrezorTxInput: Equatable, Hashable { + public static func ==(lhs: TrezorTxInput, rhs: TrezorTxInput) -> Bool { + if lhs.prevHash != rhs.prevHash { return false } - if lhs.messageType != rhs.messageType { + if lhs.prevIndex != rhs.prevIndex { return false } - if lhs.data != rhs.data { + if lhs.path != rhs.path { return false } - if lhs.error != rhs.error { + if lhs.amount != rhs.amount { return false } - if lhs.errorCode != rhs.errorCode { + if lhs.scriptType != rhs.scriptType { + return false + } + if lhs.sequence != rhs.sequence { + return false + } + if lhs.origHash != rhs.origHash { + return false + } + if lhs.origIndex != rhs.origIndex { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(success) - hasher.combine(messageType) - hasher.combine(data) - hasher.combine(error) - hasher.combine(errorCode) + hasher.combine(prevHash) + hasher.combine(prevIndex) + hasher.combine(path) + hasher.combine(amount) + hasher.combine(scriptType) + hasher.combine(sequence) + hasher.combine(origHash) + hasher.combine(origIndex) } } -extension TrezorCallMessageResult: Codable {} +extension TrezorTxInput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorCallMessageResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCallMessageResult { +public struct FfiConverterTypeTrezorTxInput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTxInput { return - try TrezorCallMessageResult( - success: FfiConverterBool.read(from: &buf), - messageType: FfiConverterUInt16.read(from: &buf), - data: FfiConverterData.read(from: &buf), - error: FfiConverterString.read(from: &buf), - errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) + try TrezorTxInput( + prevHash: FfiConverterString.read(from: &buf), + prevIndex: FfiConverterUInt32.read(from: &buf), + path: FfiConverterString.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + scriptType: FfiConverterTypeTrezorScriptType.read(from: &buf), + sequence: FfiConverterOptionUInt32.read(from: &buf), + origHash: FfiConverterOptionString.read(from: &buf), + origIndex: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: TrezorCallMessageResult, into buf: inout [UInt8]) { - FfiConverterBool.write(value.success, into: &buf) - FfiConverterUInt16.write(value.messageType, into: &buf) - FfiConverterData.write(value.data, into: &buf) - FfiConverterString.write(value.error, into: &buf) - FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) + public static func write(_ value: TrezorTxInput, into buf: inout [UInt8]) { + FfiConverterString.write(value.prevHash, into: &buf) + FfiConverterUInt32.write(value.prevIndex, into: &buf) + FfiConverterString.write(value.path, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterTypeTrezorScriptType.write(value.scriptType, into: &buf) + FfiConverterOptionUInt32.write(value.sequence, into: &buf) + FfiConverterOptionString.write(value.origHash, into: &buf) + FfiConverterOptionUInt32.write(value.origIndex, into: &buf) } } @@ -12843,156 +15233,156 @@ public struct FfiConverterTypeTrezorCallMessageResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorCallMessageResult_lift(_ buf: RustBuffer) throws -> TrezorCallMessageResult { - return try FfiConverterTypeTrezorCallMessageResult.lift(buf) +public func FfiConverterTypeTrezorTxInput_lift(_ buf: RustBuffer) throws -> TrezorTxInput { + return try FfiConverterTypeTrezorTxInput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorCallMessageResult_lower(_ value: TrezorCallMessageResult) -> RustBuffer { - return FfiConverterTypeTrezorCallMessageResult.lower(value) +public func FfiConverterTypeTrezorTxInput_lower(_ value: TrezorTxInput) -> RustBuffer { + return FfiConverterTypeTrezorTxInput.lower(value) } /** - * Device information exposed to FFI. + * Transaction output for signing. */ -public struct TrezorDeviceInfo { +public struct TrezorTxOutput { /** - * Unique identifier for the device + * Destination address (for external outputs) */ - public var id: String + public var address: String? /** - * Transport type (USB or Bluetooth) + * BIP32 path (for change outputs) */ - public var transportType: TrezorTransportType + public var path: String? /** - * Device name (from BLE advertisement or USB descriptor) + * Amount in satoshis */ - public var name: String? + public var amount: UInt64 /** - * Transport-specific path (used internally for connection) + * Script type (for change outputs) */ - public var path: String + public var scriptType: TrezorScriptType? /** - * Device label (set by user during device setup) + * OP_RETURN data (hex encoded, for data outputs) */ - public var label: String? + public var opReturnData: String? /** - * Device model (e.g., "T2", "Safe 5", "Safe 7") + * Original transaction hash for RBF replacement (hex encoded) */ - public var model: String? + public var origHash: String? /** - * Whether the device is in bootloader mode + * Original output index for RBF replacement */ - public var isBootloader: Bool + public var origIndex: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Unique identifier for the device - */id: String, + * Destination address (for external outputs) + */address: String?, /** - * Transport type (USB or Bluetooth) - */transportType: TrezorTransportType, + * BIP32 path (for change outputs) + */path: String?, /** - * Device name (from BLE advertisement or USB descriptor) - */name: String?, + * Amount in satoshis + */amount: UInt64, /** - * Transport-specific path (used internally for connection) - */path: String, + * Script type (for change outputs) + */scriptType: TrezorScriptType?, /** - * Device label (set by user during device setup) - */label: String?, + * OP_RETURN data (hex encoded, for data outputs) + */opReturnData: String?, /** - * Device model (e.g., "T2", "Safe 5", "Safe 7") - */model: String?, + * Original transaction hash for RBF replacement (hex encoded) + */origHash: String?, /** - * Whether the device is in bootloader mode - */isBootloader: Bool) { - self.id = id - self.transportType = transportType - self.name = name + * Original output index for RBF replacement + */origIndex: UInt32?) { + self.address = address self.path = path - self.label = label - self.model = model - self.isBootloader = isBootloader + self.amount = amount + self.scriptType = scriptType + self.opReturnData = opReturnData + self.origHash = origHash + self.origIndex = origIndex } } #if compiler(>=6) -extension TrezorDeviceInfo: Sendable {} +extension TrezorTxOutput: Sendable {} #endif -extension TrezorDeviceInfo: Equatable, Hashable { - public static func ==(lhs: TrezorDeviceInfo, rhs: TrezorDeviceInfo) -> Bool { - if lhs.id != rhs.id { +extension TrezorTxOutput: Equatable, Hashable { + public static func ==(lhs: TrezorTxOutput, rhs: TrezorTxOutput) -> Bool { + if lhs.address != rhs.address { return false } - if lhs.transportType != rhs.transportType { + if lhs.path != rhs.path { return false } - if lhs.name != rhs.name { + if lhs.amount != rhs.amount { return false } - if lhs.path != rhs.path { + if lhs.scriptType != rhs.scriptType { return false } - if lhs.label != rhs.label { + if lhs.opReturnData != rhs.opReturnData { return false } - if lhs.model != rhs.model { + if lhs.origHash != rhs.origHash { return false } - if lhs.isBootloader != rhs.isBootloader { + if lhs.origIndex != rhs.origIndex { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(transportType) - hasher.combine(name) + hasher.combine(address) hasher.combine(path) - hasher.combine(label) - hasher.combine(model) - hasher.combine(isBootloader) + hasher.combine(amount) + hasher.combine(scriptType) + hasher.combine(opReturnData) + hasher.combine(origHash) + hasher.combine(origIndex) } } -extension TrezorDeviceInfo: Codable {} +extension TrezorTxOutput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorDeviceInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorDeviceInfo { +public struct FfiConverterTypeTrezorTxOutput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTxOutput { return - try TrezorDeviceInfo( - id: FfiConverterString.read(from: &buf), - transportType: FfiConverterTypeTrezorTransportType.read(from: &buf), - name: FfiConverterOptionString.read(from: &buf), - path: FfiConverterString.read(from: &buf), - label: FfiConverterOptionString.read(from: &buf), - model: FfiConverterOptionString.read(from: &buf), - isBootloader: FfiConverterBool.read(from: &buf) + try TrezorTxOutput( + address: FfiConverterOptionString.read(from: &buf), + path: FfiConverterOptionString.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + scriptType: FfiConverterOptionTypeTrezorScriptType.read(from: &buf), + opReturnData: FfiConverterOptionString.read(from: &buf), + origHash: FfiConverterOptionString.read(from: &buf), + origIndex: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: TrezorDeviceInfo, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterTypeTrezorTransportType.write(value.transportType, into: &buf) - FfiConverterOptionString.write(value.name, into: &buf) - FfiConverterString.write(value.path, into: &buf) - FfiConverterOptionString.write(value.label, into: &buf) - FfiConverterOptionString.write(value.model, into: &buf) - FfiConverterBool.write(value.isBootloader, into: &buf) + public static func write(_ value: TrezorTxOutput, into buf: inout [UInt8]) { + FfiConverterOptionString.write(value.address, into: &buf) + FfiConverterOptionString.write(value.path, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterOptionTypeTrezorScriptType.write(value.scriptType, into: &buf) + FfiConverterOptionString.write(value.opReturnData, into: &buf) + FfiConverterOptionString.write(value.origHash, into: &buf) + FfiConverterOptionUInt32.write(value.origIndex, into: &buf) } } @@ -13000,246 +15390,114 @@ public struct FfiConverterTypeTrezorDeviceInfo: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorDeviceInfo_lift(_ buf: RustBuffer) throws -> TrezorDeviceInfo { - return try FfiConverterTypeTrezorDeviceInfo.lift(buf) +public func FfiConverterTypeTrezorTxOutput_lift(_ buf: RustBuffer) throws -> TrezorTxOutput { + return try FfiConverterTypeTrezorTxOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorDeviceInfo_lower(_ value: TrezorDeviceInfo) -> RustBuffer { - return FfiConverterTypeTrezorDeviceInfo.lower(value) +public func FfiConverterTypeTrezorTxOutput_lower(_ value: TrezorTxOutput) -> RustBuffer { + return FfiConverterTypeTrezorTxOutput.lower(value) } /** - * Device features after initialization. + * Parameters for verifying a message signature. */ -public struct TrezorFeatures { - /** - * Vendor string - */ - public var vendor: String? - /** - * Device model - */ - public var model: String? - /** - * Device label (set by user during device setup) - */ - public var label: String? - /** - * Device ID (unique per device) - */ - public var deviceId: String? - /** - * Major firmware version - */ - public var majorVersion: UInt32? - /** - * Minor firmware version - */ - public var minorVersion: UInt32? - /** - * Patch firmware version - */ - public var patchVersion: UInt32? - /** - * Whether PIN protection is enabled - */ - public var pinProtection: Bool? - /** - * Whether the device is currently unlocked. When PIN protection is enabled - * and this is `Some(false)`, mobile callers should back off and ask the - * user to unlock the Trezor instead of repeatedly reconnecting. - */ - public var unlocked: Bool? - /** - * Whether passphrase protection is enabled - */ - public var passphraseProtection: Bool? +public struct TrezorVerifyMessageParams { /** - * Whether the device is initialized with a seed + * Bitcoin address that signed the message */ - public var initialized: Bool? + public var address: String /** - * Whether the device needs backup + * Signature (base64 encoded) */ - public var needsBackup: Bool? + public var signature: String /** - * Whether the device can accept passphrase entry on the device itself - * (`Capability_PassphraseEntry`). When false/None, use host entry only. - */ - public var passphraseEntryCapable: Bool? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * Vendor string - */vendor: String?, - /** - * Device model - */model: String?, - /** - * Device label (set by user during device setup) - */label: String?, - /** - * Device ID (unique per device) - */deviceId: String?, - /** - * Major firmware version - */majorVersion: UInt32?, - /** - * Minor firmware version - */minorVersion: UInt32?, - /** - * Patch firmware version - */patchVersion: UInt32?, - /** - * Whether PIN protection is enabled - */pinProtection: Bool?, - /** - * Whether the device is currently unlocked. When PIN protection is enabled - * and this is `Some(false)`, mobile callers should back off and ask the - * user to unlock the Trezor instead of repeatedly reconnecting. - */unlocked: Bool?, + * Original message + */ + public var message: String + /** + * Coin network (default: Bitcoin) + */ + public var coin: TrezorCoinType? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( /** - * Whether passphrase protection is enabled - */passphraseProtection: Bool?, + * Bitcoin address that signed the message + */address: String, /** - * Whether the device is initialized with a seed - */initialized: Bool?, + * Signature (base64 encoded) + */signature: String, /** - * Whether the device needs backup - */needsBackup: Bool?, + * Original message + */message: String, /** - * Whether the device can accept passphrase entry on the device itself - * (`Capability_PassphraseEntry`). When false/None, use host entry only. - */passphraseEntryCapable: Bool?) { - self.vendor = vendor - self.model = model - self.label = label - self.deviceId = deviceId - self.majorVersion = majorVersion - self.minorVersion = minorVersion - self.patchVersion = patchVersion - self.pinProtection = pinProtection - self.unlocked = unlocked - self.passphraseProtection = passphraseProtection - self.initialized = initialized - self.needsBackup = needsBackup - self.passphraseEntryCapable = passphraseEntryCapable + * Coin network (default: Bitcoin) + */coin: TrezorCoinType?) { + self.address = address + self.signature = signature + self.message = message + self.coin = coin } } #if compiler(>=6) -extension TrezorFeatures: Sendable {} +extension TrezorVerifyMessageParams: Sendable {} #endif -extension TrezorFeatures: Equatable, Hashable { - public static func ==(lhs: TrezorFeatures, rhs: TrezorFeatures) -> Bool { - if lhs.vendor != rhs.vendor { - return false - } - if lhs.model != rhs.model { - return false - } - if lhs.label != rhs.label { - return false - } - if lhs.deviceId != rhs.deviceId { - return false - } - if lhs.majorVersion != rhs.majorVersion { - return false - } - if lhs.minorVersion != rhs.minorVersion { - return false - } - if lhs.patchVersion != rhs.patchVersion { - return false - } - if lhs.pinProtection != rhs.pinProtection { - return false - } - if lhs.unlocked != rhs.unlocked { - return false - } - if lhs.passphraseProtection != rhs.passphraseProtection { +extension TrezorVerifyMessageParams: Equatable, Hashable { + public static func ==(lhs: TrezorVerifyMessageParams, rhs: TrezorVerifyMessageParams) -> Bool { + if lhs.address != rhs.address { return false } - if lhs.initialized != rhs.initialized { + if lhs.signature != rhs.signature { return false } - if lhs.needsBackup != rhs.needsBackup { + if lhs.message != rhs.message { return false } - if lhs.passphraseEntryCapable != rhs.passphraseEntryCapable { + if lhs.coin != rhs.coin { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(vendor) - hasher.combine(model) - hasher.combine(label) - hasher.combine(deviceId) - hasher.combine(majorVersion) - hasher.combine(minorVersion) - hasher.combine(patchVersion) - hasher.combine(pinProtection) - hasher.combine(unlocked) - hasher.combine(passphraseProtection) - hasher.combine(initialized) - hasher.combine(needsBackup) - hasher.combine(passphraseEntryCapable) + hasher.combine(address) + hasher.combine(signature) + hasher.combine(message) + hasher.combine(coin) } } -extension TrezorFeatures: Codable {} +extension TrezorVerifyMessageParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorFeatures: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorFeatures { +public struct FfiConverterTypeTrezorVerifyMessageParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorVerifyMessageParams { return - try TrezorFeatures( - vendor: FfiConverterOptionString.read(from: &buf), - model: FfiConverterOptionString.read(from: &buf), - label: FfiConverterOptionString.read(from: &buf), - deviceId: FfiConverterOptionString.read(from: &buf), - majorVersion: FfiConverterOptionUInt32.read(from: &buf), - minorVersion: FfiConverterOptionUInt32.read(from: &buf), - patchVersion: FfiConverterOptionUInt32.read(from: &buf), - pinProtection: FfiConverterOptionBool.read(from: &buf), - unlocked: FfiConverterOptionBool.read(from: &buf), - passphraseProtection: FfiConverterOptionBool.read(from: &buf), - initialized: FfiConverterOptionBool.read(from: &buf), - needsBackup: FfiConverterOptionBool.read(from: &buf), - passphraseEntryCapable: FfiConverterOptionBool.read(from: &buf) + try TrezorVerifyMessageParams( + address: FfiConverterString.read(from: &buf), + signature: FfiConverterString.read(from: &buf), + message: FfiConverterString.read(from: &buf), + coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf) ) } - public static func write(_ value: TrezorFeatures, into buf: inout [UInt8]) { - FfiConverterOptionString.write(value.vendor, into: &buf) - FfiConverterOptionString.write(value.model, into: &buf) - FfiConverterOptionString.write(value.label, into: &buf) - FfiConverterOptionString.write(value.deviceId, into: &buf) - FfiConverterOptionUInt32.write(value.majorVersion, into: &buf) - FfiConverterOptionUInt32.write(value.minorVersion, into: &buf) - FfiConverterOptionUInt32.write(value.patchVersion, into: &buf) - FfiConverterOptionBool.write(value.pinProtection, into: &buf) - FfiConverterOptionBool.write(value.unlocked, into: &buf) - FfiConverterOptionBool.write(value.passphraseProtection, into: &buf) - FfiConverterOptionBool.write(value.initialized, into: &buf) - FfiConverterOptionBool.write(value.needsBackup, into: &buf) - FfiConverterOptionBool.write(value.passphraseEntryCapable, into: &buf) + public static func write(_ value: TrezorVerifyMessageParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.signature, into: &buf) + FfiConverterString.write(value.message, into: &buf) + FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) } } @@ -13247,114 +15505,128 @@ public struct FfiConverterTypeTrezorFeatures: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorFeatures_lift(_ buf: RustBuffer) throws -> TrezorFeatures { - return try FfiConverterTypeTrezorFeatures.lift(buf) +public func FfiConverterTypeTrezorVerifyMessageParams_lift(_ buf: RustBuffer) throws -> TrezorVerifyMessageParams { + return try FfiConverterTypeTrezorVerifyMessageParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorFeatures_lower(_ value: TrezorFeatures) -> RustBuffer { - return FfiConverterTypeTrezorFeatures.lower(value) +public func FfiConverterTypeTrezorVerifyMessageParams_lower(_ value: TrezorVerifyMessageParams) -> RustBuffer { + return FfiConverterTypeTrezorVerifyMessageParams.lower(value) } /** - * Parameters for getting an address from the device. + * A transaction input with full details. */ -public struct TrezorGetAddressParams { +public struct TxDetailInput { /** - * BIP32 path (e.g., "m/84'/0'/0'/0/0") + * Previous output transaction ID (hex) */ - public var path: String + public var txid: String /** - * Coin network (default: Bitcoin) + * Previous output index */ - public var coin: TrezorCoinType? + public var vout: UInt32 /** - * Whether to display the address on the device for confirmation + * Sequence number */ - public var showOnTrezor: Bool + public var sequence: UInt32 /** - * Script type (auto-detected from path if not specified) + * Script signature (hex-encoded) */ - public var scriptType: TrezorScriptType? + public var scriptSig: String + /** + * Witness stack (each element hex-encoded) + */ + public var witness: [String] // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * BIP32 path (e.g., "m/84'/0'/0'/0/0") - */path: String, + * Previous output transaction ID (hex) + */txid: String, /** - * Coin network (default: Bitcoin) - */coin: TrezorCoinType?, + * Previous output index + */vout: UInt32, /** - * Whether to display the address on the device for confirmation - */showOnTrezor: Bool, + * Sequence number + */sequence: UInt32, /** - * Script type (auto-detected from path if not specified) - */scriptType: TrezorScriptType?) { - self.path = path - self.coin = coin - self.showOnTrezor = showOnTrezor - self.scriptType = scriptType + * Script signature (hex-encoded) + */scriptSig: String, + /** + * Witness stack (each element hex-encoded) + */witness: [String]) { + self.txid = txid + self.vout = vout + self.sequence = sequence + self.scriptSig = scriptSig + self.witness = witness } } #if compiler(>=6) -extension TrezorGetAddressParams: Sendable {} +extension TxDetailInput: Sendable {} #endif -extension TrezorGetAddressParams: Equatable, Hashable { - public static func ==(lhs: TrezorGetAddressParams, rhs: TrezorGetAddressParams) -> Bool { - if lhs.path != rhs.path { +extension TxDetailInput: Equatable, Hashable { + public static func ==(lhs: TxDetailInput, rhs: TxDetailInput) -> Bool { + if lhs.txid != rhs.txid { return false } - if lhs.coin != rhs.coin { + if lhs.vout != rhs.vout { return false } - if lhs.showOnTrezor != rhs.showOnTrezor { + if lhs.sequence != rhs.sequence { return false } - if lhs.scriptType != rhs.scriptType { + if lhs.scriptSig != rhs.scriptSig { + return false + } + if lhs.witness != rhs.witness { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(coin) - hasher.combine(showOnTrezor) - hasher.combine(scriptType) + hasher.combine(txid) + hasher.combine(vout) + hasher.combine(sequence) + hasher.combine(scriptSig) + hasher.combine(witness) } } -extension TrezorGetAddressParams: Codable {} +extension TxDetailInput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorGetAddressParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorGetAddressParams { +public struct FfiConverterTypeTxDetailInput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDetailInput { return - try TrezorGetAddressParams( - path: FfiConverterString.read(from: &buf), - coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), - showOnTrezor: FfiConverterBool.read(from: &buf), - scriptType: FfiConverterOptionTypeTrezorScriptType.read(from: &buf) + try TxDetailInput( + txid: FfiConverterString.read(from: &buf), + vout: FfiConverterUInt32.read(from: &buf), + sequence: FfiConverterUInt32.read(from: &buf), + scriptSig: FfiConverterString.read(from: &buf), + witness: FfiConverterSequenceString.read(from: &buf) ) } - public static func write(_ value: TrezorGetAddressParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) - FfiConverterBool.write(value.showOnTrezor, into: &buf) - FfiConverterOptionTypeTrezorScriptType.write(value.scriptType, into: &buf) + public static func write(_ value: TxDetailInput, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt32.write(value.vout, into: &buf) + FfiConverterUInt32.write(value.sequence, into: &buf) + FfiConverterString.write(value.scriptSig, into: &buf) + FfiConverterSequenceString.write(value.witness, into: &buf) } } @@ -13362,100 +15634,114 @@ public struct FfiConverterTypeTrezorGetAddressParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorGetAddressParams_lift(_ buf: RustBuffer) throws -> TrezorGetAddressParams { - return try FfiConverterTypeTrezorGetAddressParams.lift(buf) +public func FfiConverterTypeTxDetailInput_lift(_ buf: RustBuffer) throws -> TxDetailInput { + return try FfiConverterTypeTxDetailInput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorGetAddressParams_lower(_ value: TrezorGetAddressParams) -> RustBuffer { - return FfiConverterTypeTrezorGetAddressParams.lower(value) +public func FfiConverterTypeTxDetailInput_lower(_ value: TxDetailInput) -> RustBuffer { + return FfiConverterTypeTxDetailInput.lower(value) } /** - * Parameters for getting a public key from the device. + * A transaction output with full details. */ -public struct TrezorGetPublicKeyParams { +public struct TxDetailOutput { /** - * BIP32 path (e.g., "m/84'/0'/0'") + * Output value in sats */ - public var path: String + public var value: UInt64 /** - * Coin network (default: Bitcoin) + * Script public key (hex-encoded) */ - public var coin: TrezorCoinType? + public var scriptPubkey: String /** - * Whether to display on device for confirmation + * Decoded address (None if script is not decodable to an address) */ - public var showOnTrezor: Bool + public var address: String? + /** + * Whether this output belongs to the queried wallet + */ + public var isMine: Bool // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * BIP32 path (e.g., "m/84'/0'/0'") - */path: String, + * Output value in sats + */value: UInt64, /** - * Coin network (default: Bitcoin) - */coin: TrezorCoinType?, + * Script public key (hex-encoded) + */scriptPubkey: String, /** - * Whether to display on device for confirmation - */showOnTrezor: Bool) { - self.path = path - self.coin = coin - self.showOnTrezor = showOnTrezor + * Decoded address (None if script is not decodable to an address) + */address: String?, + /** + * Whether this output belongs to the queried wallet + */isMine: Bool) { + self.value = value + self.scriptPubkey = scriptPubkey + self.address = address + self.isMine = isMine } } #if compiler(>=6) -extension TrezorGetPublicKeyParams: Sendable {} +extension TxDetailOutput: Sendable {} #endif -extension TrezorGetPublicKeyParams: Equatable, Hashable { - public static func ==(lhs: TrezorGetPublicKeyParams, rhs: TrezorGetPublicKeyParams) -> Bool { - if lhs.path != rhs.path { +extension TxDetailOutput: Equatable, Hashable { + public static func ==(lhs: TxDetailOutput, rhs: TxDetailOutput) -> Bool { + if lhs.value != rhs.value { return false } - if lhs.coin != rhs.coin { + if lhs.scriptPubkey != rhs.scriptPubkey { return false } - if lhs.showOnTrezor != rhs.showOnTrezor { + if lhs.address != rhs.address { + return false + } + if lhs.isMine != rhs.isMine { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(coin) - hasher.combine(showOnTrezor) + hasher.combine(value) + hasher.combine(scriptPubkey) + hasher.combine(address) + hasher.combine(isMine) } } -extension TrezorGetPublicKeyParams: Codable {} +extension TxDetailOutput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorGetPublicKeyParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorGetPublicKeyParams { +public struct FfiConverterTypeTxDetailOutput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDetailOutput { return - try TrezorGetPublicKeyParams( - path: FfiConverterString.read(from: &buf), - coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), - showOnTrezor: FfiConverterBool.read(from: &buf) + try TxDetailOutput( + value: FfiConverterUInt64.read(from: &buf), + scriptPubkey: FfiConverterString.read(from: &buf), + address: FfiConverterOptionString.read(from: &buf), + isMine: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: TrezorGetPublicKeyParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) - FfiConverterBool.write(value.showOnTrezor, into: &buf) + public static func write(_ value: TxDetailOutput, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.value, into: &buf) + FfiConverterString.write(value.scriptPubkey, into: &buf) + FfiConverterOptionString.write(value.address, into: &buf) + FfiConverterBool.write(value.isMine, into: &buf) } } @@ -13463,128 +15749,128 @@ public struct FfiConverterTypeTrezorGetPublicKeyParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorGetPublicKeyParams_lift(_ buf: RustBuffer) throws -> TrezorGetPublicKeyParams { - return try FfiConverterTypeTrezorGetPublicKeyParams.lift(buf) +public func FfiConverterTypeTxDetailOutput_lift(_ buf: RustBuffer) throws -> TxDetailOutput { + return try FfiConverterTypeTxDetailOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorGetPublicKeyParams_lower(_ value: TrezorGetPublicKeyParams) -> RustBuffer { - return FfiConverterTypeTrezorGetPublicKeyParams.lower(value) +public func FfiConverterTypeTxDetailOutput_lower(_ value: TxDetailOutput) -> RustBuffer { + return FfiConverterTypeTxDetailOutput.lower(value) } /** - * Previous transaction data (for non-SegWit input verification). + * Details about a transaction input. */ -public struct TrezorPrevTx { +public struct TxInput { /** - * Transaction hash (hex encoded) + * The transaction ID of the previous output being spent. */ - public var hash: String + public var txid: String /** - * Transaction version + * The output index of the previous output being spent. */ - public var version: UInt32 + public var vout: UInt32 /** - * Lock time + * The script signature (hex-encoded). */ - public var lockTime: UInt32 + public var scriptsig: String /** - * Transaction inputs + * The witness stack (hex-encoded strings). */ - public var inputs: [TrezorPrevTxInput] + public var witness: [String] /** - * Transaction outputs + * The sequence number. */ - public var outputs: [TrezorPrevTxOutput] + public var sequence: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Transaction hash (hex encoded) - */hash: String, + * The transaction ID of the previous output being spent. + */txid: String, /** - * Transaction version - */version: UInt32, + * The output index of the previous output being spent. + */vout: UInt32, /** - * Lock time - */lockTime: UInt32, + * The script signature (hex-encoded). + */scriptsig: String, /** - * Transaction inputs - */inputs: [TrezorPrevTxInput], + * The witness stack (hex-encoded strings). + */witness: [String], /** - * Transaction outputs - */outputs: [TrezorPrevTxOutput]) { - self.hash = hash - self.version = version - self.lockTime = lockTime - self.inputs = inputs - self.outputs = outputs + * The sequence number. + */sequence: UInt32) { + self.txid = txid + self.vout = vout + self.scriptsig = scriptsig + self.witness = witness + self.sequence = sequence } } #if compiler(>=6) -extension TrezorPrevTx: Sendable {} +extension TxInput: Sendable {} #endif -extension TrezorPrevTx: Equatable, Hashable { - public static func ==(lhs: TrezorPrevTx, rhs: TrezorPrevTx) -> Bool { - if lhs.hash != rhs.hash { +extension TxInput: Equatable, Hashable { + public static func ==(lhs: TxInput, rhs: TxInput) -> Bool { + if lhs.txid != rhs.txid { return false } - if lhs.version != rhs.version { + if lhs.vout != rhs.vout { return false } - if lhs.lockTime != rhs.lockTime { + if lhs.scriptsig != rhs.scriptsig { return false } - if lhs.inputs != rhs.inputs { + if lhs.witness != rhs.witness { return false } - if lhs.outputs != rhs.outputs { + if lhs.sequence != rhs.sequence { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(hash) - hasher.combine(version) - hasher.combine(lockTime) - hasher.combine(inputs) - hasher.combine(outputs) + hasher.combine(txid) + hasher.combine(vout) + hasher.combine(scriptsig) + hasher.combine(witness) + hasher.combine(sequence) } } -extension TrezorPrevTx: Codable {} +extension TxInput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorPrevTx: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTx { +public struct FfiConverterTypeTxInput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxInput { return - try TrezorPrevTx( - hash: FfiConverterString.read(from: &buf), - version: FfiConverterUInt32.read(from: &buf), - lockTime: FfiConverterUInt32.read(from: &buf), - inputs: FfiConverterSequenceTypeTrezorPrevTxInput.read(from: &buf), - outputs: FfiConverterSequenceTypeTrezorPrevTxOutput.read(from: &buf) + try TxInput( + txid: FfiConverterString.read(from: &buf), + vout: FfiConverterUInt32.read(from: &buf), + scriptsig: FfiConverterString.read(from: &buf), + witness: FfiConverterSequenceString.read(from: &buf), + sequence: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: TrezorPrevTx, into buf: inout [UInt8]) { - FfiConverterString.write(value.hash, into: &buf) - FfiConverterUInt32.write(value.version, into: &buf) - FfiConverterUInt32.write(value.lockTime, into: &buf) - FfiConverterSequenceTypeTrezorPrevTxInput.write(value.inputs, into: &buf) - FfiConverterSequenceTypeTrezorPrevTxOutput.write(value.outputs, into: &buf) + public static func write(_ value: TxInput, into buf: inout [UInt8]) { + FfiConverterString.write(value.txid, into: &buf) + FfiConverterUInt32.write(value.vout, into: &buf) + FfiConverterString.write(value.scriptsig, into: &buf) + FfiConverterSequenceString.write(value.witness, into: &buf) + FfiConverterUInt32.write(value.sequence, into: &buf) } } @@ -13592,114 +15878,128 @@ public struct FfiConverterTypeTrezorPrevTx: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTx_lift(_ buf: RustBuffer) throws -> TrezorPrevTx { - return try FfiConverterTypeTrezorPrevTx.lift(buf) +public func FfiConverterTypeTxInput_lift(_ buf: RustBuffer) throws -> TxInput { + return try FfiConverterTypeTxInput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTx_lower(_ value: TrezorPrevTx) -> RustBuffer { - return FfiConverterTypeTrezorPrevTx.lower(value) +public func FfiConverterTypeTxInput_lower(_ value: TxInput) -> RustBuffer { + return FfiConverterTypeTxInput.lower(value) } /** - * Input of a previous transaction. + * Details about a transaction output. */ -public struct TrezorPrevTxInput { +public struct TxOutput { /** - * Previous transaction hash (hex encoded) + * The script public key (hex-encoded). */ - public var prevHash: String + public var scriptpubkey: String /** - * Previous output index + * The script public key type (e.g., "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"). */ - public var prevIndex: UInt32 + public var scriptpubkeyType: String? /** - * Script signature (hex encoded) + * The address corresponding to this script (if decodable). */ - public var scriptSig: String + public var scriptpubkeyAddress: String? /** - * Sequence number + * The value in satoshis. */ - public var sequence: UInt32 + public var value: Int64 + /** + * The output index in the transaction. + */ + public var n: UInt32 // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Previous transaction hash (hex encoded) - */prevHash: String, + * The script public key (hex-encoded). + */scriptpubkey: String, + /** + * The script public key type (e.g., "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"). + */scriptpubkeyType: String?, /** - * Previous output index - */prevIndex: UInt32, + * The address corresponding to this script (if decodable). + */scriptpubkeyAddress: String?, /** - * Script signature (hex encoded) - */scriptSig: String, + * The value in satoshis. + */value: Int64, /** - * Sequence number - */sequence: UInt32) { - self.prevHash = prevHash - self.prevIndex = prevIndex - self.scriptSig = scriptSig - self.sequence = sequence + * The output index in the transaction. + */n: UInt32) { + self.scriptpubkey = scriptpubkey + self.scriptpubkeyType = scriptpubkeyType + self.scriptpubkeyAddress = scriptpubkeyAddress + self.value = value + self.n = n } } #if compiler(>=6) -extension TrezorPrevTxInput: Sendable {} +extension TxOutput: Sendable {} #endif -extension TrezorPrevTxInput: Equatable, Hashable { - public static func ==(lhs: TrezorPrevTxInput, rhs: TrezorPrevTxInput) -> Bool { - if lhs.prevHash != rhs.prevHash { +extension TxOutput: Equatable, Hashable { + public static func ==(lhs: TxOutput, rhs: TxOutput) -> Bool { + if lhs.scriptpubkey != rhs.scriptpubkey { return false } - if lhs.prevIndex != rhs.prevIndex { + if lhs.scriptpubkeyType != rhs.scriptpubkeyType { return false } - if lhs.scriptSig != rhs.scriptSig { + if lhs.scriptpubkeyAddress != rhs.scriptpubkeyAddress { return false } - if lhs.sequence != rhs.sequence { + if lhs.value != rhs.value { + return false + } + if lhs.n != rhs.n { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(prevHash) - hasher.combine(prevIndex) - hasher.combine(scriptSig) - hasher.combine(sequence) + hasher.combine(scriptpubkey) + hasher.combine(scriptpubkeyType) + hasher.combine(scriptpubkeyAddress) + hasher.combine(value) + hasher.combine(n) } } -extension TrezorPrevTxInput: Codable {} +extension TxOutput: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorPrevTxInput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTxInput { +public struct FfiConverterTypeTxOutput: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxOutput { return - try TrezorPrevTxInput( - prevHash: FfiConverterString.read(from: &buf), - prevIndex: FfiConverterUInt32.read(from: &buf), - scriptSig: FfiConverterString.read(from: &buf), - sequence: FfiConverterUInt32.read(from: &buf) + try TxOutput( + scriptpubkey: FfiConverterString.read(from: &buf), + scriptpubkeyType: FfiConverterOptionString.read(from: &buf), + scriptpubkeyAddress: FfiConverterOptionString.read(from: &buf), + value: FfiConverterInt64.read(from: &buf), + n: FfiConverterUInt32.read(from: &buf) ) } - public static func write(_ value: TrezorPrevTxInput, into buf: inout [UInt8]) { - FfiConverterString.write(value.prevHash, into: &buf) - FfiConverterUInt32.write(value.prevIndex, into: &buf) - FfiConverterString.write(value.scriptSig, into: &buf) - FfiConverterUInt32.write(value.sequence, into: &buf) + public static func write(_ value: TxOutput, into buf: inout [UInt8]) { + FfiConverterString.write(value.scriptpubkey, into: &buf) + FfiConverterOptionString.write(value.scriptpubkeyType, into: &buf) + FfiConverterOptionString.write(value.scriptpubkeyAddress, into: &buf) + FfiConverterInt64.write(value.value, into: &buf) + FfiConverterUInt32.write(value.n, into: &buf) } } @@ -13707,86 +16007,100 @@ public struct FfiConverterTypeTrezorPrevTxInput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTxInput_lift(_ buf: RustBuffer) throws -> TrezorPrevTxInput { - return try FfiConverterTypeTrezorPrevTxInput.lift(buf) +public func FfiConverterTypeTxOutput_lift(_ buf: RustBuffer) throws -> TxOutput { + return try FfiConverterTypeTxOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTxInput_lower(_ value: TrezorPrevTxInput) -> RustBuffer { - return FfiConverterTypeTrezorPrevTxInput.lower(value) +public func FfiConverterTypeTxOutput_lower(_ value: TxOutput) -> RustBuffer { + return FfiConverterTypeTxOutput.lower(value) } /** - * Output of a previous transaction. + * Current state after accepting a scanned UR frame. */ -public struct TrezorPrevTxOutput { +public struct UrDecoderStatus { /** - * Amount in satoshis + * Estimated completion from 0.0 through 1.0. */ - public var amount: UInt64 + public var progress: Double /** - * Script pubkey (hex encoded) + * Fountain source-fragment count, or 1 for a single-part UR. */ - public var scriptPubkey: String + public var fragmentCount: UInt32 + /** + * Present once the complete message has been decoded. + */ + public var payload: UrPayload? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Amount in satoshis - */amount: UInt64, + * Estimated completion from 0.0 through 1.0. + */progress: Double, /** - * Script pubkey (hex encoded) - */scriptPubkey: String) { - self.amount = amount - self.scriptPubkey = scriptPubkey + * Fountain source-fragment count, or 1 for a single-part UR. + */fragmentCount: UInt32, + /** + * Present once the complete message has been decoded. + */payload: UrPayload?) { + self.progress = progress + self.fragmentCount = fragmentCount + self.payload = payload } } #if compiler(>=6) -extension TrezorPrevTxOutput: Sendable {} +extension UrDecoderStatus: Sendable {} #endif -extension TrezorPrevTxOutput: Equatable, Hashable { - public static func ==(lhs: TrezorPrevTxOutput, rhs: TrezorPrevTxOutput) -> Bool { - if lhs.amount != rhs.amount { +extension UrDecoderStatus: Equatable, Hashable { + public static func ==(lhs: UrDecoderStatus, rhs: UrDecoderStatus) -> Bool { + if lhs.progress != rhs.progress { return false } - if lhs.scriptPubkey != rhs.scriptPubkey { + if lhs.fragmentCount != rhs.fragmentCount { + return false + } + if lhs.payload != rhs.payload { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(amount) - hasher.combine(scriptPubkey) + hasher.combine(progress) + hasher.combine(fragmentCount) + hasher.combine(payload) } } -extension TrezorPrevTxOutput: Codable {} +extension UrDecoderStatus: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorPrevTxOutput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPrevTxOutput { +public struct FfiConverterTypeUrDecoderStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrDecoderStatus { return - try TrezorPrevTxOutput( - amount: FfiConverterUInt64.read(from: &buf), - scriptPubkey: FfiConverterString.read(from: &buf) + try UrDecoderStatus( + progress: FfiConverterDouble.read(from: &buf), + fragmentCount: FfiConverterUInt32.read(from: &buf), + payload: FfiConverterOptionTypeUrPayload.read(from: &buf) ) } - public static func write(_ value: TrezorPrevTxOutput, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterString.write(value.scriptPubkey, into: &buf) + public static func write(_ value: UrDecoderStatus, into buf: inout [UInt8]) { + FfiConverterDouble.write(value.progress, into: &buf) + FfiConverterUInt32.write(value.fragmentCount, into: &buf) + FfiConverterOptionTypeUrPayload.write(value.payload, into: &buf) } } @@ -13794,156 +16108,119 @@ public struct FfiConverterTypeTrezorPrevTxOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTxOutput_lift(_ buf: RustBuffer) throws -> TrezorPrevTxOutput { - return try FfiConverterTypeTrezorPrevTxOutput.lift(buf) +public func FfiConverterTypeUrDecoderStatus_lift(_ buf: RustBuffer) throws -> UrDecoderStatus { + return try FfiConverterTypeUrDecoderStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPrevTxOutput_lower(_ value: TrezorPrevTxOutput) -> RustBuffer { - return FfiConverterTypeTrezorPrevTxOutput.lower(value) +public func FfiConverterTypeUrDecoderStatus_lower(_ value: UrDecoderStatus) -> RustBuffer { + return FfiConverterTypeUrDecoderStatus.lower(value) } -/** - * Public key response from device. - */ -public struct TrezorPublicKeyResponse { - /** - * Extended public key (xpub) - */ - public var xpub: String - /** - * The serialized path (e.g., "m/84'/0'/0'") - */ - public var path: String - /** - * Compressed public key (hex encoded) - */ - public var publicKey: String - /** - * Chain code (hex encoded) - */ - public var chainCode: String - /** - * Parent key fingerprint - */ - public var fingerprint: UInt32 - /** - * Derivation depth - */ - public var depth: UInt32 - /** - * Master root fingerprint (from the device's master seed) - */ - public var rootFingerprint: UInt32? +public struct UsdtDeposit { + public var id: String + public var network: String + public var asset: String + public var amount: UInt64? + public var sourceTx: String + public var status: String + public var code: String? + public var refundTx: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Extended public key (xpub) - */xpub: String, - /** - * The serialized path (e.g., "m/84'/0'/0'") - */path: String, - /** - * Compressed public key (hex encoded) - */publicKey: String, - /** - * Chain code (hex encoded) - */chainCode: String, - /** - * Parent key fingerprint - */fingerprint: UInt32, - /** - * Derivation depth - */depth: UInt32, - /** - * Master root fingerprint (from the device's master seed) - */rootFingerprint: UInt32?) { - self.xpub = xpub - self.path = path - self.publicKey = publicKey - self.chainCode = chainCode - self.fingerprint = fingerprint - self.depth = depth - self.rootFingerprint = rootFingerprint + public init(id: String, network: String, asset: String, amount: UInt64?, sourceTx: String, status: String, code: String?, refundTx: String?) { + self.id = id + self.network = network + self.asset = asset + self.amount = amount + self.sourceTx = sourceTx + self.status = status + self.code = code + self.refundTx = refundTx } } #if compiler(>=6) -extension TrezorPublicKeyResponse: Sendable {} +extension UsdtDeposit: Sendable {} #endif -extension TrezorPublicKeyResponse: Equatable, Hashable { - public static func ==(lhs: TrezorPublicKeyResponse, rhs: TrezorPublicKeyResponse) -> Bool { - if lhs.xpub != rhs.xpub { +extension UsdtDeposit: Equatable, Hashable { + public static func ==(lhs: UsdtDeposit, rhs: UsdtDeposit) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.path != rhs.path { + if lhs.network != rhs.network { return false } - if lhs.publicKey != rhs.publicKey { + if lhs.asset != rhs.asset { return false } - if lhs.chainCode != rhs.chainCode { + if lhs.amount != rhs.amount { return false } - if lhs.fingerprint != rhs.fingerprint { + if lhs.sourceTx != rhs.sourceTx { return false } - if lhs.depth != rhs.depth { + if lhs.status != rhs.status { return false } - if lhs.rootFingerprint != rhs.rootFingerprint { + if lhs.code != rhs.code { + return false + } + if lhs.refundTx != rhs.refundTx { return false } return true } - - public func hash(into hasher: inout Hasher) { - hasher.combine(xpub) - hasher.combine(path) - hasher.combine(publicKey) - hasher.combine(chainCode) - hasher.combine(fingerprint) - hasher.combine(depth) - hasher.combine(rootFingerprint) - } + + public func hash(into hasher: inout Hasher) { + hasher.combine(id) + hasher.combine(network) + hasher.combine(asset) + hasher.combine(amount) + hasher.combine(sourceTx) + hasher.combine(status) + hasher.combine(code) + hasher.combine(refundTx) + } } -extension TrezorPublicKeyResponse: Codable {} +extension UsdtDeposit: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorPublicKeyResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorPublicKeyResponse { +public struct FfiConverterTypeUsdtDeposit: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDeposit { return - try TrezorPublicKeyResponse( - xpub: FfiConverterString.read(from: &buf), - path: FfiConverterString.read(from: &buf), - publicKey: FfiConverterString.read(from: &buf), - chainCode: FfiConverterString.read(from: &buf), - fingerprint: FfiConverterUInt32.read(from: &buf), - depth: FfiConverterUInt32.read(from: &buf), - rootFingerprint: FfiConverterOptionUInt32.read(from: &buf) + try UsdtDeposit( + id: FfiConverterString.read(from: &buf), + network: FfiConverterString.read(from: &buf), + asset: FfiConverterString.read(from: &buf), + amount: FfiConverterOptionUInt64.read(from: &buf), + sourceTx: FfiConverterString.read(from: &buf), + status: FfiConverterString.read(from: &buf), + code: FfiConverterOptionString.read(from: &buf), + refundTx: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: TrezorPublicKeyResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.xpub, into: &buf) - FfiConverterString.write(value.path, into: &buf) - FfiConverterString.write(value.publicKey, into: &buf) - FfiConverterString.write(value.chainCode, into: &buf) - FfiConverterUInt32.write(value.fingerprint, into: &buf) - FfiConverterUInt32.write(value.depth, into: &buf) - FfiConverterOptionUInt32.write(value.rootFingerprint, into: &buf) + public static func write(_ value: UsdtDeposit, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.network, into: &buf) + FfiConverterString.write(value.asset, into: &buf) + FfiConverterOptionUInt64.write(value.amount, into: &buf) + FfiConverterString.write(value.sourceTx, into: &buf) + FfiConverterString.write(value.status, into: &buf) + FfiConverterOptionString.write(value.code, into: &buf) + FfiConverterOptionString.write(value.refundTx, into: &buf) } } @@ -13951,100 +16228,127 @@ public struct FfiConverterTypeTrezorPublicKeyResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPublicKeyResponse_lift(_ buf: RustBuffer) throws -> TrezorPublicKeyResponse { - return try FfiConverterTypeTrezorPublicKeyResponse.lift(buf) +public func FfiConverterTypeUsdtDeposit_lift(_ buf: RustBuffer) throws -> UsdtDeposit { + return try FfiConverterTypeUsdtDeposit.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorPublicKeyResponse_lower(_ value: TrezorPublicKeyResponse) -> RustBuffer { - return FfiConverterTypeTrezorPublicKeyResponse.lower(value) +public func FfiConverterTypeUsdtDeposit_lower(_ value: UsdtDeposit) -> RustBuffer { + return FfiConverterTypeUsdtDeposit.lower(value) } -/** - * Parameters for signing a message. - */ -public struct TrezorSignMessageParams { - /** - * BIP32 path for the signing key (e.g., "m/84'/0'/0'/0/0") - */ - public var path: String - /** - * Message to sign - */ - public var message: String - /** - * Coin network (default: Bitcoin) - */ - public var coin: TrezorCoinType? +public struct UsdtDepositAddress { + public var network: UsdtDepositNetwork + public var address: String + public var recipient: String + public var amount: UInt64 + public var estimatedReceived: UInt64 + public var minUsdCents: String? + public var maxUsdCents: String? + public var slippageBps: UInt32 + public var uri: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * BIP32 path for the signing key (e.g., "m/84'/0'/0'/0/0") - */path: String, - /** - * Message to sign - */message: String, - /** - * Coin network (default: Bitcoin) - */coin: TrezorCoinType?) { - self.path = path - self.message = message - self.coin = coin + public init(network: UsdtDepositNetwork, address: String, recipient: String, amount: UInt64, estimatedReceived: UInt64, minUsdCents: String?, maxUsdCents: String?, slippageBps: UInt32, uri: String) { + self.network = network + self.address = address + self.recipient = recipient + self.amount = amount + self.estimatedReceived = estimatedReceived + self.minUsdCents = minUsdCents + self.maxUsdCents = maxUsdCents + self.slippageBps = slippageBps + self.uri = uri } } #if compiler(>=6) -extension TrezorSignMessageParams: Sendable {} +extension UsdtDepositAddress: Sendable {} #endif -extension TrezorSignMessageParams: Equatable, Hashable { - public static func ==(lhs: TrezorSignMessageParams, rhs: TrezorSignMessageParams) -> Bool { - if lhs.path != rhs.path { +extension UsdtDepositAddress: Equatable, Hashable { + public static func ==(lhs: UsdtDepositAddress, rhs: UsdtDepositAddress) -> Bool { + if lhs.network != rhs.network { return false } - if lhs.message != rhs.message { + if lhs.address != rhs.address { return false } - if lhs.coin != rhs.coin { + if lhs.recipient != rhs.recipient { + return false + } + if lhs.amount != rhs.amount { + return false + } + if lhs.estimatedReceived != rhs.estimatedReceived { + return false + } + if lhs.minUsdCents != rhs.minUsdCents { + return false + } + if lhs.maxUsdCents != rhs.maxUsdCents { + return false + } + if lhs.slippageBps != rhs.slippageBps { + return false + } + if lhs.uri != rhs.uri { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(path) - hasher.combine(message) - hasher.combine(coin) + hasher.combine(network) + hasher.combine(address) + hasher.combine(recipient) + hasher.combine(amount) + hasher.combine(estimatedReceived) + hasher.combine(minUsdCents) + hasher.combine(maxUsdCents) + hasher.combine(slippageBps) + hasher.combine(uri) } } -extension TrezorSignMessageParams: Codable {} +extension UsdtDepositAddress: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorSignMessageParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignMessageParams { +public struct FfiConverterTypeUsdtDepositAddress: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositAddress { return - try TrezorSignMessageParams( - path: FfiConverterString.read(from: &buf), - message: FfiConverterString.read(from: &buf), - coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf) + try UsdtDepositAddress( + network: FfiConverterTypeUsdtDepositNetwork.read(from: &buf), + address: FfiConverterString.read(from: &buf), + recipient: FfiConverterString.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + estimatedReceived: FfiConverterUInt64.read(from: &buf), + minUsdCents: FfiConverterOptionString.read(from: &buf), + maxUsdCents: FfiConverterOptionString.read(from: &buf), + slippageBps: FfiConverterUInt32.read(from: &buf), + uri: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: TrezorSignMessageParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.path, into: &buf) - FfiConverterString.write(value.message, into: &buf) - FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) + public static func write(_ value: UsdtDepositAddress, into buf: inout [UInt8]) { + FfiConverterTypeUsdtDepositNetwork.write(value.network, into: &buf) + FfiConverterString.write(value.address, into: &buf) + FfiConverterString.write(value.recipient, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterUInt64.write(value.estimatedReceived, into: &buf) + FfiConverterOptionString.write(value.minUsdCents, into: &buf) + FfiConverterOptionString.write(value.maxUsdCents, into: &buf) + FfiConverterUInt32.write(value.slippageBps, into: &buf) + FfiConverterString.write(value.uri, into: &buf) } } @@ -14052,142 +16356,71 @@ public struct FfiConverterTypeTrezorSignMessageParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignMessageParams_lift(_ buf: RustBuffer) throws -> TrezorSignMessageParams { - return try FfiConverterTypeTrezorSignMessageParams.lift(buf) +public func FfiConverterTypeUsdtDepositAddress_lift(_ buf: RustBuffer) throws -> UsdtDepositAddress { + return try FfiConverterTypeUsdtDepositAddress.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignMessageParams_lower(_ value: TrezorSignMessageParams) -> RustBuffer { - return FfiConverterTypeTrezorSignMessageParams.lower(value) +public func FfiConverterTypeUsdtDepositAddress_lower(_ value: UsdtDepositAddress) -> RustBuffer { + return FfiConverterTypeUsdtDepositAddress.lower(value) } -/** - * Parameters for signing a transaction. - */ -public struct TrezorSignTxParams { - /** - * Transaction inputs - */ - public var inputs: [TrezorTxInput] - /** - * Transaction outputs - */ - public var outputs: [TrezorTxOutput] - /** - * Coin network (default: Bitcoin) - */ - public var coin: TrezorCoinType? - /** - * Lock time (default: 0) - */ - public var lockTime: UInt32? - /** - * Version (default: 2) - */ - public var version: UInt32? - /** - * Previous transactions (for non-SegWit input verification) - */ - public var prevTxs: [TrezorPrevTx] +public struct UsdtDepositDetail { + public var deposit: UsdtDeposit + public var order: UsdtDepositOrder? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Transaction inputs - */inputs: [TrezorTxInput], - /** - * Transaction outputs - */outputs: [TrezorTxOutput], - /** - * Coin network (default: Bitcoin) - */coin: TrezorCoinType?, - /** - * Lock time (default: 0) - */lockTime: UInt32?, - /** - * Version (default: 2) - */version: UInt32?, - /** - * Previous transactions (for non-SegWit input verification) - */prevTxs: [TrezorPrevTx]) { - self.inputs = inputs - self.outputs = outputs - self.coin = coin - self.lockTime = lockTime - self.version = version - self.prevTxs = prevTxs + public init(deposit: UsdtDeposit, order: UsdtDepositOrder?) { + self.deposit = deposit + self.order = order } } #if compiler(>=6) -extension TrezorSignTxParams: Sendable {} +extension UsdtDepositDetail: Sendable {} #endif -extension TrezorSignTxParams: Equatable, Hashable { - public static func ==(lhs: TrezorSignTxParams, rhs: TrezorSignTxParams) -> Bool { - if lhs.inputs != rhs.inputs { +extension UsdtDepositDetail: Equatable, Hashable { + public static func ==(lhs: UsdtDepositDetail, rhs: UsdtDepositDetail) -> Bool { + if lhs.deposit != rhs.deposit { return false } - if lhs.outputs != rhs.outputs { - return false - } - if lhs.coin != rhs.coin { - return false - } - if lhs.lockTime != rhs.lockTime { - return false - } - if lhs.version != rhs.version { - return false - } - if lhs.prevTxs != rhs.prevTxs { + if lhs.order != rhs.order { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(inputs) - hasher.combine(outputs) - hasher.combine(coin) - hasher.combine(lockTime) - hasher.combine(version) - hasher.combine(prevTxs) + hasher.combine(deposit) + hasher.combine(order) } } -extension TrezorSignTxParams: Codable {} +extension UsdtDepositDetail: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorSignTxParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignTxParams { +public struct FfiConverterTypeUsdtDepositDetail: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositDetail { return - try TrezorSignTxParams( - inputs: FfiConverterSequenceTypeTrezorTxInput.read(from: &buf), - outputs: FfiConverterSequenceTypeTrezorTxOutput.read(from: &buf), - coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf), - lockTime: FfiConverterOptionUInt32.read(from: &buf), - version: FfiConverterOptionUInt32.read(from: &buf), - prevTxs: FfiConverterSequenceTypeTrezorPrevTx.read(from: &buf) + try UsdtDepositDetail( + deposit: FfiConverterTypeUsdtDeposit.read(from: &buf), + order: FfiConverterOptionTypeUsdtDepositOrder.read(from: &buf) ) } - public static func write(_ value: TrezorSignTxParams, into buf: inout [UInt8]) { - FfiConverterSequenceTypeTrezorTxInput.write(value.inputs, into: &buf) - FfiConverterSequenceTypeTrezorTxOutput.write(value.outputs, into: &buf) - FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) - FfiConverterOptionUInt32.write(value.lockTime, into: &buf) - FfiConverterOptionUInt32.write(value.version, into: &buf) - FfiConverterSequenceTypeTrezorPrevTx.write(value.prevTxs, into: &buf) + public static func write(_ value: UsdtDepositDetail, into buf: inout [UInt8]) { + FfiConverterTypeUsdtDeposit.write(value.deposit, into: &buf) + FfiConverterOptionTypeUsdtDepositOrder.write(value.order, into: &buf) } } @@ -14195,86 +16428,103 @@ public struct FfiConverterTypeTrezorSignTxParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignTxParams_lift(_ buf: RustBuffer) throws -> TrezorSignTxParams { - return try FfiConverterTypeTrezorSignTxParams.lift(buf) +public func FfiConverterTypeUsdtDepositDetail_lift(_ buf: RustBuffer) throws -> UsdtDepositDetail { + return try FfiConverterTypeUsdtDepositDetail.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignTxParams_lower(_ value: TrezorSignTxParams) -> RustBuffer { - return FfiConverterTypeTrezorSignTxParams.lower(value) +public func FfiConverterTypeUsdtDepositDetail_lower(_ value: UsdtDepositDetail) -> RustBuffer { + return FfiConverterTypeUsdtDepositDetail.lower(value) } -/** - * Response from signing a message. - */ -public struct TrezorSignedMessageResponse { - /** - * Bitcoin address that signed the message - */ - public var address: String - /** - * Signature (base64 encoded) - */ - public var signature: String +public struct UsdtDepositOrder { + public var status: String + public var amountIn: UInt64? + public var amountOut: UInt64? + public var destinationTx: String? + public var refundTx: String? + public var code: String? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Bitcoin address that signed the message - */address: String, - /** - * Signature (base64 encoded) - */signature: String) { - self.address = address - self.signature = signature + public init(status: String, amountIn: UInt64?, amountOut: UInt64?, destinationTx: String?, refundTx: String?, code: String?) { + self.status = status + self.amountIn = amountIn + self.amountOut = amountOut + self.destinationTx = destinationTx + self.refundTx = refundTx + self.code = code } } #if compiler(>=6) -extension TrezorSignedMessageResponse: Sendable {} +extension UsdtDepositOrder: Sendable {} #endif -extension TrezorSignedMessageResponse: Equatable, Hashable { - public static func ==(lhs: TrezorSignedMessageResponse, rhs: TrezorSignedMessageResponse) -> Bool { - if lhs.address != rhs.address { +extension UsdtDepositOrder: Equatable, Hashable { + public static func ==(lhs: UsdtDepositOrder, rhs: UsdtDepositOrder) -> Bool { + if lhs.status != rhs.status { return false } - if lhs.signature != rhs.signature { + if lhs.amountIn != rhs.amountIn { + return false + } + if lhs.amountOut != rhs.amountOut { + return false + } + if lhs.destinationTx != rhs.destinationTx { + return false + } + if lhs.refundTx != rhs.refundTx { + return false + } + if lhs.code != rhs.code { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(signature) + hasher.combine(status) + hasher.combine(amountIn) + hasher.combine(amountOut) + hasher.combine(destinationTx) + hasher.combine(refundTx) + hasher.combine(code) } } -extension TrezorSignedMessageResponse: Codable {} +extension UsdtDepositOrder: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorSignedMessageResponse: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignedMessageResponse { +public struct FfiConverterTypeUsdtDepositOrder: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositOrder { return - try TrezorSignedMessageResponse( - address: FfiConverterString.read(from: &buf), - signature: FfiConverterString.read(from: &buf) + try UsdtDepositOrder( + status: FfiConverterString.read(from: &buf), + amountIn: FfiConverterOptionUInt64.read(from: &buf), + amountOut: FfiConverterOptionUInt64.read(from: &buf), + destinationTx: FfiConverterOptionString.read(from: &buf), + refundTx: FfiConverterOptionString.read(from: &buf), + code: FfiConverterOptionString.read(from: &buf) ) } - public static func write(_ value: TrezorSignedMessageResponse, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.signature, into: &buf) + public static func write(_ value: UsdtDepositOrder, into buf: inout [UInt8]) { + FfiConverterString.write(value.status, into: &buf) + FfiConverterOptionUInt64.write(value.amountIn, into: &buf) + FfiConverterOptionUInt64.write(value.amountOut, into: &buf) + FfiConverterOptionString.write(value.destinationTx, into: &buf) + FfiConverterOptionString.write(value.refundTx, into: &buf) + FfiConverterOptionString.write(value.code, into: &buf) } } @@ -14282,100 +16532,71 @@ public struct FfiConverterTypeTrezorSignedMessageResponse: FfiConverterRustBuffe #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignedMessageResponse_lift(_ buf: RustBuffer) throws -> TrezorSignedMessageResponse { - return try FfiConverterTypeTrezorSignedMessageResponse.lift(buf) +public func FfiConverterTypeUsdtDepositOrder_lift(_ buf: RustBuffer) throws -> UsdtDepositOrder { + return try FfiConverterTypeUsdtDepositOrder.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignedMessageResponse_lower(_ value: TrezorSignedMessageResponse) -> RustBuffer { - return FfiConverterTypeTrezorSignedMessageResponse.lower(value) +public func FfiConverterTypeUsdtDepositOrder_lower(_ value: UsdtDepositOrder) -> RustBuffer { + return FfiConverterTypeUsdtDepositOrder.lower(value) } -/** - * Signed transaction result. - */ -public struct TrezorSignedTx { - /** - * Signatures for each input (hex encoded) - */ - public var signatures: [String] - /** - * Serialized transaction (hex) - */ - public var serializedTx: String - /** - * Broadcast transaction ID (populated when push=true) - */ - public var txid: String? +public struct UsdtDepositPage { + public var deposits: [UsdtDeposit] + public var nextOffset: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Signatures for each input (hex encoded) - */signatures: [String], - /** - * Serialized transaction (hex) - */serializedTx: String, - /** - * Broadcast transaction ID (populated when push=true) - */txid: String?) { - self.signatures = signatures - self.serializedTx = serializedTx - self.txid = txid + public init(deposits: [UsdtDeposit], nextOffset: UInt32?) { + self.deposits = deposits + self.nextOffset = nextOffset } } #if compiler(>=6) -extension TrezorSignedTx: Sendable {} +extension UsdtDepositPage: Sendable {} #endif -extension TrezorSignedTx: Equatable, Hashable { - public static func ==(lhs: TrezorSignedTx, rhs: TrezorSignedTx) -> Bool { - if lhs.signatures != rhs.signatures { - return false - } - if lhs.serializedTx != rhs.serializedTx { +extension UsdtDepositPage: Equatable, Hashable { + public static func ==(lhs: UsdtDepositPage, rhs: UsdtDepositPage) -> Bool { + if lhs.deposits != rhs.deposits { return false } - if lhs.txid != rhs.txid { + if lhs.nextOffset != rhs.nextOffset { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(signatures) - hasher.combine(serializedTx) - hasher.combine(txid) + hasher.combine(deposits) + hasher.combine(nextOffset) } } -extension TrezorSignedTx: Codable {} +extension UsdtDepositPage: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorSignedTx: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorSignedTx { +public struct FfiConverterTypeUsdtDepositPage: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositPage { return - try TrezorSignedTx( - signatures: FfiConverterSequenceString.read(from: &buf), - serializedTx: FfiConverterString.read(from: &buf), - txid: FfiConverterOptionString.read(from: &buf) + try UsdtDepositPage( + deposits: FfiConverterSequenceTypeUsdtDeposit.read(from: &buf), + nextOffset: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: TrezorSignedTx, into buf: inout [UInt8]) { - FfiConverterSequenceString.write(value.signatures, into: &buf) - FfiConverterString.write(value.serializedTx, into: &buf) - FfiConverterOptionString.write(value.txid, into: &buf) + public static func write(_ value: UsdtDepositPage, into buf: inout [UInt8]) { + FfiConverterSequenceTypeUsdtDeposit.write(value.deposits, into: &buf) + FfiConverterOptionUInt32.write(value.nextOffset, into: &buf) } } @@ -14383,114 +16604,71 @@ public struct FfiConverterTypeTrezorSignedTx: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignedTx_lift(_ buf: RustBuffer) throws -> TrezorSignedTx { - return try FfiConverterTypeTrezorSignedTx.lift(buf) +public func FfiConverterTypeUsdtDepositPage_lift(_ buf: RustBuffer) throws -> UsdtDepositPage { + return try FfiConverterTypeUsdtDepositPage.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorSignedTx_lower(_ value: TrezorSignedTx) -> RustBuffer { - return FfiConverterTypeTrezorSignedTx.lower(value) +public func FfiConverterTypeUsdtDepositPage_lower(_ value: UsdtDepositPage) -> RustBuffer { + return FfiConverterTypeUsdtDepositPage.lower(value) } -/** - * Result from a transport read operation - */ -public struct TrezorTransportReadResult { - /** - * Whether the read succeeded - */ - public var success: Bool - /** - * Data read (empty on failure) - */ - public var data: Data - /** - * Error message (empty on success) - */ - public var error: String - /** - * Structured error code (None on success or when the native error is generic) - */ - public var errorCode: TrezorTransportErrorCode? +public struct UsdtPaymentRequest { + public var recipient: String + public var amount: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Whether the read succeeded - */success: Bool, - /** - * Data read (empty on failure) - */data: Data, - /** - * Error message (empty on success) - */error: String, - /** - * Structured error code (None on success or when the native error is generic) - */errorCode: TrezorTransportErrorCode?) { - self.success = success - self.data = data - self.error = error - self.errorCode = errorCode + public init(recipient: String, amount: UInt64?) { + self.recipient = recipient + self.amount = amount } } #if compiler(>=6) -extension TrezorTransportReadResult: Sendable {} +extension UsdtPaymentRequest: Sendable {} #endif -extension TrezorTransportReadResult: Equatable, Hashable { - public static func ==(lhs: TrezorTransportReadResult, rhs: TrezorTransportReadResult) -> Bool { - if lhs.success != rhs.success { - return false - } - if lhs.data != rhs.data { - return false - } - if lhs.error != rhs.error { +extension UsdtPaymentRequest: Equatable, Hashable { + public static func ==(lhs: UsdtPaymentRequest, rhs: UsdtPaymentRequest) -> Bool { + if lhs.recipient != rhs.recipient { return false } - if lhs.errorCode != rhs.errorCode { + if lhs.amount != rhs.amount { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(success) - hasher.combine(data) - hasher.combine(error) - hasher.combine(errorCode) + hasher.combine(recipient) + hasher.combine(amount) } } -extension TrezorTransportReadResult: Codable {} +extension UsdtPaymentRequest: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportReadResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportReadResult { +public struct FfiConverterTypeUsdtPaymentRequest: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtPaymentRequest { return - try TrezorTransportReadResult( - success: FfiConverterBool.read(from: &buf), - data: FfiConverterData.read(from: &buf), - error: FfiConverterString.read(from: &buf), - errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) + try UsdtPaymentRequest( + recipient: FfiConverterString.read(from: &buf), + amount: FfiConverterOptionUInt64.read(from: &buf) ) } - public static func write(_ value: TrezorTransportReadResult, into buf: inout [UInt8]) { - FfiConverterBool.write(value.success, into: &buf) - FfiConverterData.write(value.data, into: &buf) - FfiConverterString.write(value.error, into: &buf) - FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) + public static func write(_ value: UsdtPaymentRequest, into buf: inout [UInt8]) { + FfiConverterString.write(value.recipient, into: &buf) + FfiConverterOptionUInt64.write(value.amount, into: &buf) } } @@ -14498,100 +16676,111 @@ public struct FfiConverterTypeTrezorTransportReadResult: FfiConverterRustBuffer #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportReadResult_lift(_ buf: RustBuffer) throws -> TrezorTransportReadResult { - return try FfiConverterTypeTrezorTransportReadResult.lift(buf) +public func FfiConverterTypeUsdtPaymentRequest_lift(_ buf: RustBuffer) throws -> UsdtPaymentRequest { + return try FfiConverterTypeUsdtPaymentRequest.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportReadResult_lower(_ value: TrezorTransportReadResult) -> RustBuffer { - return FfiConverterTypeTrezorTransportReadResult.lower(value) +public func FfiConverterTypeUsdtPaymentRequest_lower(_ value: UsdtPaymentRequest) -> RustBuffer { + return FfiConverterTypeUsdtPaymentRequest.lower(value) } -/** - * Result from a transport write or open operation - */ -public struct TrezorTransportWriteResult { - /** - * Whether the operation succeeded - */ - public var success: Bool - /** - * Error message (empty on success) - */ - public var error: String - /** - * Structured error code (None on success or when the native error is generic) - */ - public var errorCode: TrezorTransportErrorCode? +public struct UsdtQuote { + public var id: String + public var recipient: String + public var destination: UsdtDestination + public var amount: UInt64 + public var receivedAmount: UInt64 + public var maximumFee: UInt64 + public var expiresAt: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Whether the operation succeeded - */success: Bool, - /** - * Error message (empty on success) - */error: String, - /** - * Structured error code (None on success or when the native error is generic) - */errorCode: TrezorTransportErrorCode?) { - self.success = success - self.error = error - self.errorCode = errorCode + public init(id: String, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, maximumFee: UInt64, expiresAt: UInt64) { + self.id = id + self.recipient = recipient + self.destination = destination + self.amount = amount + self.receivedAmount = receivedAmount + self.maximumFee = maximumFee + self.expiresAt = expiresAt } } #if compiler(>=6) -extension TrezorTransportWriteResult: Sendable {} +extension UsdtQuote: Sendable {} #endif -extension TrezorTransportWriteResult: Equatable, Hashable { - public static func ==(lhs: TrezorTransportWriteResult, rhs: TrezorTransportWriteResult) -> Bool { - if lhs.success != rhs.success { +extension UsdtQuote: Equatable, Hashable { + public static func ==(lhs: UsdtQuote, rhs: UsdtQuote) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.error != rhs.error { + if lhs.recipient != rhs.recipient { return false } - if lhs.errorCode != rhs.errorCode { + if lhs.destination != rhs.destination { + return false + } + if lhs.amount != rhs.amount { + return false + } + if lhs.receivedAmount != rhs.receivedAmount { + return false + } + if lhs.maximumFee != rhs.maximumFee { + return false + } + if lhs.expiresAt != rhs.expiresAt { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(success) - hasher.combine(error) - hasher.combine(errorCode) + hasher.combine(id) + hasher.combine(recipient) + hasher.combine(destination) + hasher.combine(amount) + hasher.combine(receivedAmount) + hasher.combine(maximumFee) + hasher.combine(expiresAt) } } -extension TrezorTransportWriteResult: Codable {} +extension UsdtQuote: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportWriteResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportWriteResult { +public struct FfiConverterTypeUsdtQuote: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtQuote { return - try TrezorTransportWriteResult( - success: FfiConverterBool.read(from: &buf), - error: FfiConverterString.read(from: &buf), - errorCode: FfiConverterOptionTypeTrezorTransportErrorCode.read(from: &buf) + try UsdtQuote( + id: FfiConverterString.read(from: &buf), + recipient: FfiConverterString.read(from: &buf), + destination: FfiConverterTypeUsdtDestination.read(from: &buf), + amount: FfiConverterUInt64.read(from: &buf), + receivedAmount: FfiConverterUInt64.read(from: &buf), + maximumFee: FfiConverterUInt64.read(from: &buf), + expiresAt: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: TrezorTransportWriteResult, into buf: inout [UInt8]) { - FfiConverterBool.write(value.success, into: &buf) - FfiConverterString.write(value.error, into: &buf) - FfiConverterOptionTypeTrezorTransportErrorCode.write(value.errorCode, into: &buf) + public static func write(_ value: UsdtQuote, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.recipient, into: &buf) + FfiConverterTypeUsdtDestination.write(value.destination, into: &buf) + FfiConverterUInt64.write(value.amount, into: &buf) + FfiConverterUInt64.write(value.receivedAmount, into: &buf) + FfiConverterUInt64.write(value.maximumFee, into: &buf) + FfiConverterUInt64.write(value.expiresAt, into: &buf) } } @@ -14599,170 +16788,167 @@ public struct FfiConverterTypeTrezorTransportWriteResult: FfiConverterRustBuffer #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportWriteResult_lift(_ buf: RustBuffer) throws -> TrezorTransportWriteResult { - return try FfiConverterTypeTrezorTransportWriteResult.lift(buf) +public func FfiConverterTypeUsdtQuote_lift(_ buf: RustBuffer) throws -> UsdtQuote { + return try FfiConverterTypeUsdtQuote.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportWriteResult_lower(_ value: TrezorTransportWriteResult) -> RustBuffer { - return FfiConverterTypeTrezorTransportWriteResult.lower(value) +public func FfiConverterTypeUsdtQuote_lower(_ value: UsdtQuote) -> RustBuffer { + return FfiConverterTypeUsdtQuote.lower(value) } -/** - * Transaction input for signing. - */ -public struct TrezorTxInput { - /** - * Previous transaction hash (hex, 32 bytes) - */ - public var prevHash: String - /** - * Previous output index - */ - public var prevIndex: UInt32 - /** - * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") - */ - public var path: String - /** - * Amount in satoshis - */ - public var amount: UInt64 - /** - * Script type - */ - public var scriptType: TrezorScriptType - /** - * Sequence number (default: 0xFFFFFFFD for RBF) - */ - public var sequence: UInt32? - /** - * Original transaction hash for RBF replacement (hex encoded) - */ - public var origHash: String? - /** - * Original input index for RBF replacement - */ - public var origIndex: UInt32? +public struct UsdtTransfer { + public var id: String + public var txHash: String + public var userOperationHash: String? + public var bridgeGuid: String? + public var recipient: String + public var destination: UsdtDestination + public var amount: UInt64 + public var receivedAmount: UInt64 + public var bridgeFee: UInt64 + public var fee: UInt64? + public var isIncoming: Bool + public var status: UsdtTransferStatus + public var timestamp: UInt64 + public var explorerUrl: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Previous transaction hash (hex, 32 bytes) - */prevHash: String, - /** - * Previous output index - */prevIndex: UInt32, - /** - * BIP32 derivation path (e.g., "m/84'/0'/0'/0/0") - */path: String, - /** - * Amount in satoshis - */amount: UInt64, - /** - * Script type - */scriptType: TrezorScriptType, - /** - * Sequence number (default: 0xFFFFFFFD for RBF) - */sequence: UInt32?, - /** - * Original transaction hash for RBF replacement (hex encoded) - */origHash: String?, - /** - * Original input index for RBF replacement - */origIndex: UInt32?) { - self.prevHash = prevHash - self.prevIndex = prevIndex - self.path = path + public init(id: String, txHash: String, userOperationHash: String?, bridgeGuid: String?, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, bridgeFee: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { + self.id = id + self.txHash = txHash + self.userOperationHash = userOperationHash + self.bridgeGuid = bridgeGuid + self.recipient = recipient + self.destination = destination self.amount = amount - self.scriptType = scriptType - self.sequence = sequence - self.origHash = origHash - self.origIndex = origIndex + self.receivedAmount = receivedAmount + self.bridgeFee = bridgeFee + self.fee = fee + self.isIncoming = isIncoming + self.status = status + self.timestamp = timestamp + self.explorerUrl = explorerUrl } } #if compiler(>=6) -extension TrezorTxInput: Sendable {} +extension UsdtTransfer: Sendable {} #endif -extension TrezorTxInput: Equatable, Hashable { - public static func ==(lhs: TrezorTxInput, rhs: TrezorTxInput) -> Bool { - if lhs.prevHash != rhs.prevHash { +extension UsdtTransfer: Equatable, Hashable { + public static func ==(lhs: UsdtTransfer, rhs: UsdtTransfer) -> Bool { + if lhs.id != rhs.id { return false } - if lhs.prevIndex != rhs.prevIndex { + if lhs.txHash != rhs.txHash { return false } - if lhs.path != rhs.path { + if lhs.userOperationHash != rhs.userOperationHash { + return false + } + if lhs.bridgeGuid != rhs.bridgeGuid { + return false + } + if lhs.recipient != rhs.recipient { + return false + } + if lhs.destination != rhs.destination { return false } if lhs.amount != rhs.amount { return false } - if lhs.scriptType != rhs.scriptType { + if lhs.receivedAmount != rhs.receivedAmount { return false } - if lhs.sequence != rhs.sequence { + if lhs.bridgeFee != rhs.bridgeFee { return false } - if lhs.origHash != rhs.origHash { + if lhs.fee != rhs.fee { return false } - if lhs.origIndex != rhs.origIndex { + if lhs.isIncoming != rhs.isIncoming { + return false + } + if lhs.status != rhs.status { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.explorerUrl != rhs.explorerUrl { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(prevHash) - hasher.combine(prevIndex) - hasher.combine(path) + hasher.combine(id) + hasher.combine(txHash) + hasher.combine(userOperationHash) + hasher.combine(bridgeGuid) + hasher.combine(recipient) + hasher.combine(destination) hasher.combine(amount) - hasher.combine(scriptType) - hasher.combine(sequence) - hasher.combine(origHash) - hasher.combine(origIndex) + hasher.combine(receivedAmount) + hasher.combine(bridgeFee) + hasher.combine(fee) + hasher.combine(isIncoming) + hasher.combine(status) + hasher.combine(timestamp) + hasher.combine(explorerUrl) } } -extension TrezorTxInput: Codable {} +extension UsdtTransfer: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTxInput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTxInput { +public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtTransfer { return - try TrezorTxInput( - prevHash: FfiConverterString.read(from: &buf), - prevIndex: FfiConverterUInt32.read(from: &buf), - path: FfiConverterString.read(from: &buf), + try UsdtTransfer( + id: FfiConverterString.read(from: &buf), + txHash: FfiConverterString.read(from: &buf), + userOperationHash: FfiConverterOptionString.read(from: &buf), + bridgeGuid: FfiConverterOptionString.read(from: &buf), + recipient: FfiConverterString.read(from: &buf), + destination: FfiConverterTypeUsdtDestination.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf), - scriptType: FfiConverterTypeTrezorScriptType.read(from: &buf), - sequence: FfiConverterOptionUInt32.read(from: &buf), - origHash: FfiConverterOptionString.read(from: &buf), - origIndex: FfiConverterOptionUInt32.read(from: &buf) + receivedAmount: FfiConverterUInt64.read(from: &buf), + bridgeFee: FfiConverterUInt64.read(from: &buf), + fee: FfiConverterOptionUInt64.read(from: &buf), + isIncoming: FfiConverterBool.read(from: &buf), + status: FfiConverterTypeUsdtTransferStatus.read(from: &buf), + timestamp: FfiConverterUInt64.read(from: &buf), + explorerUrl: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: TrezorTxInput, into buf: inout [UInt8]) { - FfiConverterString.write(value.prevHash, into: &buf) - FfiConverterUInt32.write(value.prevIndex, into: &buf) - FfiConverterString.write(value.path, into: &buf) + public static func write(_ value: UsdtTransfer, into buf: inout [UInt8]) { + FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.txHash, into: &buf) + FfiConverterOptionString.write(value.userOperationHash, into: &buf) + FfiConverterOptionString.write(value.bridgeGuid, into: &buf) + FfiConverterString.write(value.recipient, into: &buf) + FfiConverterTypeUsdtDestination.write(value.destination, into: &buf) FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterTypeTrezorScriptType.write(value.scriptType, into: &buf) - FfiConverterOptionUInt32.write(value.sequence, into: &buf) - FfiConverterOptionString.write(value.origHash, into: &buf) - FfiConverterOptionUInt32.write(value.origIndex, into: &buf) + FfiConverterUInt64.write(value.receivedAmount, into: &buf) + FfiConverterUInt64.write(value.bridgeFee, into: &buf) + FfiConverterOptionUInt64.write(value.fee, into: &buf) + FfiConverterBool.write(value.isIncoming, into: &buf) + FfiConverterTypeUsdtTransferStatus.write(value.status, into: &buf) + FfiConverterUInt64.write(value.timestamp, into: &buf) + FfiConverterString.write(value.explorerUrl, into: &buf) } } @@ -14770,111 +16956,46 @@ public struct FfiConverterTypeTrezorTxInput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTxInput_lift(_ buf: RustBuffer) throws -> TrezorTxInput { - return try FfiConverterTypeTrezorTxInput.lift(buf) +public func FfiConverterTypeUsdtTransfer_lift(_ buf: RustBuffer) throws -> UsdtTransfer { + return try FfiConverterTypeUsdtTransfer.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTxInput_lower(_ value: TrezorTxInput) -> RustBuffer { - return FfiConverterTypeTrezorTxInput.lower(value) +public func FfiConverterTypeUsdtTransfer_lower(_ value: UsdtTransfer) -> RustBuffer { + return FfiConverterTypeUsdtTransfer.lower(value) } -/** - * Transaction output for signing. - */ -public struct TrezorTxOutput { - /** - * Destination address (for external outputs) - */ - public var address: String? - /** - * BIP32 path (for change outputs) - */ - public var path: String? - /** - * Amount in satoshis - */ - public var amount: UInt64 - /** - * Script type (for change outputs) - */ - public var scriptType: TrezorScriptType? - /** - * OP_RETURN data (hex encoded, for data outputs) - */ - public var opReturnData: String? - /** - * Original transaction hash for RBF replacement (hex encoded) - */ - public var origHash: String? - /** - * Original output index for RBF replacement - */ - public var origIndex: UInt32? +public struct ValidationResult { + public var address: String + public var network: NetworkType + public var addressType: AddressType // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Destination address (for external outputs) - */address: String?, - /** - * BIP32 path (for change outputs) - */path: String?, - /** - * Amount in satoshis - */amount: UInt64, - /** - * Script type (for change outputs) - */scriptType: TrezorScriptType?, - /** - * OP_RETURN data (hex encoded, for data outputs) - */opReturnData: String?, - /** - * Original transaction hash for RBF replacement (hex encoded) - */origHash: String?, - /** - * Original output index for RBF replacement - */origIndex: UInt32?) { + public init(address: String, network: NetworkType, addressType: AddressType) { self.address = address - self.path = path - self.amount = amount - self.scriptType = scriptType - self.opReturnData = opReturnData - self.origHash = origHash - self.origIndex = origIndex + self.network = network + self.addressType = addressType } } #if compiler(>=6) -extension TrezorTxOutput: Sendable {} +extension ValidationResult: Sendable {} #endif -extension TrezorTxOutput: Equatable, Hashable { - public static func ==(lhs: TrezorTxOutput, rhs: TrezorTxOutput) -> Bool { +extension ValidationResult: Equatable, Hashable { + public static func ==(lhs: ValidationResult, rhs: ValidationResult) -> Bool { if lhs.address != rhs.address { return false } - if lhs.path != rhs.path { - return false - } - if lhs.amount != rhs.amount { - return false - } - if lhs.scriptType != rhs.scriptType { - return false - } - if lhs.opReturnData != rhs.opReturnData { - return false - } - if lhs.origHash != rhs.origHash { + if lhs.network != rhs.network { return false } - if lhs.origIndex != rhs.origIndex { + if lhs.addressType != rhs.addressType { return false } return true @@ -14882,44 +17003,32 @@ extension TrezorTxOutput: Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(address) - hasher.combine(path) - hasher.combine(amount) - hasher.combine(scriptType) - hasher.combine(opReturnData) - hasher.combine(origHash) - hasher.combine(origIndex) + hasher.combine(network) + hasher.combine(addressType) } } -extension TrezorTxOutput: Codable {} +extension ValidationResult: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTxOutput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTxOutput { +public struct FfiConverterTypeValidationResult: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ValidationResult { return - try TrezorTxOutput( - address: FfiConverterOptionString.read(from: &buf), - path: FfiConverterOptionString.read(from: &buf), - amount: FfiConverterUInt64.read(from: &buf), - scriptType: FfiConverterOptionTypeTrezorScriptType.read(from: &buf), - opReturnData: FfiConverterOptionString.read(from: &buf), - origHash: FfiConverterOptionString.read(from: &buf), - origIndex: FfiConverterOptionUInt32.read(from: &buf) + try ValidationResult( + address: FfiConverterString.read(from: &buf), + network: FfiConverterTypeNetworkType.read(from: &buf), + addressType: FfiConverterTypeAddressType.read(from: &buf) ) } - public static func write(_ value: TrezorTxOutput, into buf: inout [UInt8]) { - FfiConverterOptionString.write(value.address, into: &buf) - FfiConverterOptionString.write(value.path, into: &buf) - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterOptionTypeTrezorScriptType.write(value.scriptType, into: &buf) - FfiConverterOptionString.write(value.opReturnData, into: &buf) - FfiConverterOptionString.write(value.origHash, into: &buf) - FfiConverterOptionUInt32.write(value.origIndex, into: &buf) + public static func write(_ value: ValidationResult, into buf: inout [UInt8]) { + FfiConverterString.write(value.address, into: &buf) + FfiConverterTypeNetworkType.write(value.network, into: &buf) + FfiConverterTypeAddressType.write(value.addressType, into: &buf) } } @@ -14927,114 +17036,142 @@ public struct FfiConverterTypeTrezorTxOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTxOutput_lift(_ buf: RustBuffer) throws -> TrezorTxOutput { - return try FfiConverterTypeTrezorTxOutput.lift(buf) +public func FfiConverterTypeValidationResult_lift(_ buf: RustBuffer) throws -> ValidationResult { + return try FfiConverterTypeValidationResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTxOutput_lower(_ value: TrezorTxOutput) -> RustBuffer { - return FfiConverterTypeTrezorTxOutput.lower(value) +public func FfiConverterTypeValidationResult_lower(_ value: ValidationResult) -> RustBuffer { + return FfiConverterTypeValidationResult.lower(value) } /** - * Parameters for verifying a message signature. + * Balance breakdown from BDK. */ -public struct TrezorVerifyMessageParams { +public struct WalletBalance { /** - * Bitcoin address that signed the message + * Confirmed and spendable balance (sats) */ - public var address: String + public var confirmed: UInt64 + /** + * Immature coinbase outputs (sats) + */ + public var immature: UInt64 + /** + * Unconfirmed UTXOs from trusted sources (own change) (sats) + */ + public var trustedPending: UInt64 /** - * Signature (base64 encoded) + * Unconfirmed UTXOs from external sources (sats) */ - public var signature: String + public var untrustedPending: UInt64 /** - * Original message + * Total spendable: confirmed + trusted_pending (sats) */ - public var message: String + public var spendable: UInt64 /** - * Coin network (default: Bitcoin) + * Grand total: all categories (sats) */ - public var coin: TrezorCoinType? + public var total: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Bitcoin address that signed the message - */address: String, + * Confirmed and spendable balance (sats) + */confirmed: UInt64, /** - * Signature (base64 encoded) - */signature: String, + * Immature coinbase outputs (sats) + */immature: UInt64, /** - * Original message - */message: String, + * Unconfirmed UTXOs from trusted sources (own change) (sats) + */trustedPending: UInt64, /** - * Coin network (default: Bitcoin) - */coin: TrezorCoinType?) { - self.address = address - self.signature = signature - self.message = message - self.coin = coin + * Unconfirmed UTXOs from external sources (sats) + */untrustedPending: UInt64, + /** + * Total spendable: confirmed + trusted_pending (sats) + */spendable: UInt64, + /** + * Grand total: all categories (sats) + */total: UInt64) { + self.confirmed = confirmed + self.immature = immature + self.trustedPending = trustedPending + self.untrustedPending = untrustedPending + self.spendable = spendable + self.total = total } } #if compiler(>=6) -extension TrezorVerifyMessageParams: Sendable {} +extension WalletBalance: Sendable {} #endif -extension TrezorVerifyMessageParams: Equatable, Hashable { - public static func ==(lhs: TrezorVerifyMessageParams, rhs: TrezorVerifyMessageParams) -> Bool { - if lhs.address != rhs.address { +extension WalletBalance: Equatable, Hashable { + public static func ==(lhs: WalletBalance, rhs: WalletBalance) -> Bool { + if lhs.confirmed != rhs.confirmed { return false } - if lhs.signature != rhs.signature { + if lhs.immature != rhs.immature { return false } - if lhs.message != rhs.message { + if lhs.trustedPending != rhs.trustedPending { return false } - if lhs.coin != rhs.coin { + if lhs.untrustedPending != rhs.untrustedPending { + return false + } + if lhs.spendable != rhs.spendable { + return false + } + if lhs.total != rhs.total { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(signature) - hasher.combine(message) - hasher.combine(coin) + hasher.combine(confirmed) + hasher.combine(immature) + hasher.combine(trustedPending) + hasher.combine(untrustedPending) + hasher.combine(spendable) + hasher.combine(total) } } -extension TrezorVerifyMessageParams: Codable {} +extension WalletBalance: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorVerifyMessageParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorVerifyMessageParams { +public struct FfiConverterTypeWalletBalance: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletBalance { return - try TrezorVerifyMessageParams( - address: FfiConverterString.read(from: &buf), - signature: FfiConverterString.read(from: &buf), - message: FfiConverterString.read(from: &buf), - coin: FfiConverterOptionTypeTrezorCoinType.read(from: &buf) + try WalletBalance( + confirmed: FfiConverterUInt64.read(from: &buf), + immature: FfiConverterUInt64.read(from: &buf), + trustedPending: FfiConverterUInt64.read(from: &buf), + untrustedPending: FfiConverterUInt64.read(from: &buf), + spendable: FfiConverterUInt64.read(from: &buf), + total: FfiConverterUInt64.read(from: &buf) ) } - public static func write(_ value: TrezorVerifyMessageParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.signature, into: &buf) - FfiConverterString.write(value.message, into: &buf) - FfiConverterOptionTypeTrezorCoinType.write(value.coin, into: &buf) + public static func write(_ value: WalletBalance, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.confirmed, into: &buf) + FfiConverterUInt64.write(value.immature, into: &buf) + FfiConverterUInt64.write(value.trustedPending, into: &buf) + FfiConverterUInt64.write(value.untrustedPending, into: &buf) + FfiConverterUInt64.write(value.spendable, into: &buf) + FfiConverterUInt64.write(value.total, into: &buf) } } @@ -15042,128 +17179,128 @@ public struct FfiConverterTypeTrezorVerifyMessageParams: FfiConverterRustBuffer #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorVerifyMessageParams_lift(_ buf: RustBuffer) throws -> TrezorVerifyMessageParams { - return try FfiConverterTypeTrezorVerifyMessageParams.lift(buf) +public func FfiConverterTypeWalletBalance_lift(_ buf: RustBuffer) throws -> WalletBalance { + return try FfiConverterTypeWalletBalance.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorVerifyMessageParams_lower(_ value: TrezorVerifyMessageParams) -> RustBuffer { - return FfiConverterTypeTrezorVerifyMessageParams.lower(value) +public func FfiConverterTypeWalletBalance_lower(_ value: WalletBalance) -> RustBuffer { + return FfiConverterTypeWalletBalance.lower(value) } /** - * A transaction input with full details. + * Common parameters for creating and syncing a watch-only BDK wallet. */ -public struct TxDetailInput { +public struct WalletParams { /** - * Previous output transaction ID (hex) + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) */ - public var txid: String + public var extendedKey: String /** - * Previous output index + * Electrum server URL for wallet sync */ - public var vout: UInt32 + public var electrumUrl: String /** - * Sequence number + * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. */ - public var sequence: UInt32 + public var fingerprint: String? /** - * Script signature (hex-encoded) + * Bitcoin network (auto-detected from key prefix if not specified) */ - public var scriptSig: String + public var network: Network? /** - * Witness stack (each element hex-encoded) + * Override account type for ambiguous key prefixes (xpub/tpub) */ - public var witness: [String] + public var accountType: AccountType? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Previous output transaction ID (hex) - */txid: String, + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) + */extendedKey: String, /** - * Previous output index - */vout: UInt32, + * Electrum server URL for wallet sync + */electrumUrl: String, /** - * Sequence number - */sequence: UInt32, + * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. + */fingerprint: String?, /** - * Script signature (hex-encoded) - */scriptSig: String, + * Bitcoin network (auto-detected from key prefix if not specified) + */network: Network?, /** - * Witness stack (each element hex-encoded) - */witness: [String]) { - self.txid = txid - self.vout = vout - self.sequence = sequence - self.scriptSig = scriptSig - self.witness = witness + * Override account type for ambiguous key prefixes (xpub/tpub) + */accountType: AccountType?) { + self.extendedKey = extendedKey + self.electrumUrl = electrumUrl + self.fingerprint = fingerprint + self.network = network + self.accountType = accountType } } #if compiler(>=6) -extension TxDetailInput: Sendable {} +extension WalletParams: Sendable {} #endif -extension TxDetailInput: Equatable, Hashable { - public static func ==(lhs: TxDetailInput, rhs: TxDetailInput) -> Bool { - if lhs.txid != rhs.txid { +extension WalletParams: Equatable, Hashable { + public static func ==(lhs: WalletParams, rhs: WalletParams) -> Bool { + if lhs.extendedKey != rhs.extendedKey { return false } - if lhs.vout != rhs.vout { + if lhs.electrumUrl != rhs.electrumUrl { return false } - if lhs.sequence != rhs.sequence { + if lhs.fingerprint != rhs.fingerprint { return false } - if lhs.scriptSig != rhs.scriptSig { + if lhs.network != rhs.network { return false } - if lhs.witness != rhs.witness { + if lhs.accountType != rhs.accountType { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(vout) - hasher.combine(sequence) - hasher.combine(scriptSig) - hasher.combine(witness) + hasher.combine(extendedKey) + hasher.combine(electrumUrl) + hasher.combine(fingerprint) + hasher.combine(network) + hasher.combine(accountType) } } -extension TxDetailInput: Codable {} +extension WalletParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTxDetailInput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDetailInput { +public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletParams { return - try TxDetailInput( - txid: FfiConverterString.read(from: &buf), - vout: FfiConverterUInt32.read(from: &buf), - sequence: FfiConverterUInt32.read(from: &buf), - scriptSig: FfiConverterString.read(from: &buf), - witness: FfiConverterSequenceString.read(from: &buf) + try WalletParams( + extendedKey: FfiConverterString.read(from: &buf), + electrumUrl: FfiConverterString.read(from: &buf), + fingerprint: FfiConverterOptionString.read(from: &buf), + network: FfiConverterOptionTypeNetwork.read(from: &buf), + accountType: FfiConverterOptionTypeAccountType.read(from: &buf) ) } - public static func write(_ value: TxDetailInput, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt32.write(value.vout, into: &buf) - FfiConverterUInt32.write(value.sequence, into: &buf) - FfiConverterString.write(value.scriptSig, into: &buf) - FfiConverterSequenceString.write(value.witness, into: &buf) + public static func write(_ value: WalletParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.extendedKey, into: &buf) + FfiConverterString.write(value.electrumUrl, into: &buf) + FfiConverterOptionString.write(value.fingerprint, into: &buf) + FfiConverterOptionTypeNetwork.write(value.network, into: &buf) + FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) } } @@ -15171,114 +17308,160 @@ public struct FfiConverterTypeTxDetailInput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDetailInput_lift(_ buf: RustBuffer) throws -> TxDetailInput { - return try FfiConverterTypeTxDetailInput.lift(buf) +public func FfiConverterTypeWalletParams_lift(_ buf: RustBuffer) throws -> WalletParams { + return try FfiConverterTypeWalletParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDetailInput_lower(_ value: TxDetailInput) -> RustBuffer { - return FfiConverterTypeTxDetailInput.lower(value) +public func FfiConverterTypeWalletParams_lower(_ value: WalletParams) -> RustBuffer { + return FfiConverterTypeWalletParams.lower(value) } /** - * A transaction output with full details. + * Parameters for starting an xpub transaction watcher. */ -public struct TxDetailOutput { +public struct WatcherParams { /** - * Output value in sats + * Caller-supplied identifier for this watcher. */ - public var value: UInt64 + public var watcherId: String /** - * Script public key (hex-encoded) + * Wallet id that scopes the activities this watcher emits. Apps may use + * one wallet id for several account watchers and merge their snapshots. */ - public var scriptPubkey: String + public var walletId: String + /** + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). + */ + public var extendedKey: String + /** + * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). + */ + public var electrumUrl: String + /** + * Bitcoin network override (auto-detected from key prefix if None). + */ + public var network: Network? /** - * Decoded address (None if script is not decodable to an address) + * Account type override (auto-detected from key prefix if None). */ - public var address: String? + public var accountType: AccountType? /** - * Whether this output belongs to the queried wallet + * Number of unused addresses to monitor beyond the last used + * (defaults to `DEFAULT_GAP_LIMIT` when None). */ - public var isMine: Bool + public var gapLimit: UInt32? // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Output value in sats - */value: UInt64, + * Caller-supplied identifier for this watcher. + */watcherId: String, /** - * Script public key (hex-encoded) - */scriptPubkey: String, + * Wallet id that scopes the activities this watcher emits. Apps may use + * one wallet id for several account watchers and merge their snapshots. + */walletId: String, /** - * Decoded address (None if script is not decodable to an address) - */address: String?, + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). + */extendedKey: String, /** - * Whether this output belongs to the queried wallet - */isMine: Bool) { - self.value = value - self.scriptPubkey = scriptPubkey - self.address = address - self.isMine = isMine + * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). + */electrumUrl: String, + /** + * Bitcoin network override (auto-detected from key prefix if None). + */network: Network?, + /** + * Account type override (auto-detected from key prefix if None). + */accountType: AccountType?, + /** + * Number of unused addresses to monitor beyond the last used + * (defaults to `DEFAULT_GAP_LIMIT` when None). + */gapLimit: UInt32?) { + self.watcherId = watcherId + self.walletId = walletId + self.extendedKey = extendedKey + self.electrumUrl = electrumUrl + self.network = network + self.accountType = accountType + self.gapLimit = gapLimit } } #if compiler(>=6) -extension TxDetailOutput: Sendable {} +extension WatcherParams: Sendable {} #endif -extension TxDetailOutput: Equatable, Hashable { - public static func ==(lhs: TxDetailOutput, rhs: TxDetailOutput) -> Bool { - if lhs.value != rhs.value { +extension WatcherParams: Equatable, Hashable { + public static func ==(lhs: WatcherParams, rhs: WatcherParams) -> Bool { + if lhs.watcherId != rhs.watcherId { return false } - if lhs.scriptPubkey != rhs.scriptPubkey { + if lhs.walletId != rhs.walletId { return false } - if lhs.address != rhs.address { + if lhs.extendedKey != rhs.extendedKey { return false } - if lhs.isMine != rhs.isMine { + if lhs.electrumUrl != rhs.electrumUrl { + return false + } + if lhs.network != rhs.network { + return false + } + if lhs.accountType != rhs.accountType { + return false + } + if lhs.gapLimit != rhs.gapLimit { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(value) - hasher.combine(scriptPubkey) - hasher.combine(address) - hasher.combine(isMine) + hasher.combine(watcherId) + hasher.combine(walletId) + hasher.combine(extendedKey) + hasher.combine(electrumUrl) + hasher.combine(network) + hasher.combine(accountType) + hasher.combine(gapLimit) } } -extension TxDetailOutput: Codable {} +extension WatcherParams: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTxDetailOutput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDetailOutput { +public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WatcherParams { return - try TxDetailOutput( - value: FfiConverterUInt64.read(from: &buf), - scriptPubkey: FfiConverterString.read(from: &buf), - address: FfiConverterOptionString.read(from: &buf), - isMine: FfiConverterBool.read(from: &buf) + try WatcherParams( + watcherId: FfiConverterString.read(from: &buf), + walletId: FfiConverterString.read(from: &buf), + extendedKey: FfiConverterString.read(from: &buf), + electrumUrl: FfiConverterString.read(from: &buf), + network: FfiConverterOptionTypeNetwork.read(from: &buf), + accountType: FfiConverterOptionTypeAccountType.read(from: &buf), + gapLimit: FfiConverterOptionUInt32.read(from: &buf) ) } - public static func write(_ value: TxDetailOutput, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.value, into: &buf) - FfiConverterString.write(value.scriptPubkey, into: &buf) - FfiConverterOptionString.write(value.address, into: &buf) - FfiConverterBool.write(value.isMine, into: &buf) + public static func write(_ value: WatcherParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.watcherId, into: &buf) + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.extendedKey, into: &buf) + FfiConverterString.write(value.electrumUrl, into: &buf) + FfiConverterOptionTypeNetwork.write(value.network, into: &buf) + FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) + FfiConverterOptionUInt32.write(value.gapLimit, into: &buf) } } @@ -15286,128 +17469,183 @@ public struct FfiConverterTypeTxDetailOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDetailOutput_lift(_ buf: RustBuffer) throws -> TxDetailOutput { - return try FfiConverterTypeTxDetailOutput.lift(buf) +public func FfiConverterTypeWatcherParams_lift(_ buf: RustBuffer) throws -> WatcherParams { + return try FfiConverterTypeWatcherParams.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDetailOutput_lower(_ value: TxDetailOutput) -> RustBuffer { - return FfiConverterTypeTxDetailOutput.lower(value) +public func FfiConverterTypeWatcherParams_lower(_ value: WatcherParams) -> RustBuffer { + return FfiConverterTypeWatcherParams.lower(value) } /** - * Details about a transaction input. + * Errors specific to account info operations (BDK/Electrum-based). */ -public struct TxInput { +public enum AccountInfoError: Swift.Error { + + + /** - * The transaction ID of the previous output being spent. + * The provided extended public key is invalid or cannot be parsed */ - public var txid: String + case InvalidExtendedKey(errorDetails: String + ) /** - * The output index of the previous output being spent. + * The provided address is invalid */ - public var vout: UInt32 + case InvalidAddress(errorDetails: String + ) /** - * The script signature (hex-encoded). + * Electrum connection or query failed */ - public var scriptsig: String + case ElectrumError(errorDetails: String + ) /** - * The witness stack (hex-encoded strings). + * BDK wallet creation or operation error */ - public var witness: [String] + case WalletError(errorDetails: String + ) /** - * The sequence number. + * Wallet sync with Electrum failed */ - public var sequence: UInt32 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * The transaction ID of the previous output being spent. - */txid: String, - /** - * The output index of the previous output being spent. - */vout: UInt32, - /** - * The script signature (hex-encoded). - */scriptsig: String, - /** - * The witness stack (hex-encoded strings). - */witness: [String], - /** - * The sequence number. - */sequence: UInt32) { - self.txid = txid - self.vout = vout - self.scriptsig = scriptsig - self.witness = witness - self.sequence = sequence - } + case SyncError(errorDetails: String + ) + /** + * The key type/prefix is not recognized + */ + case UnsupportedKeyType(errorDetails: String + ) + /** + * Network mismatch between key prefix and specified network + */ + case NetworkMismatch(errorDetails: String + ) + /** + * Invalid transaction ID provided + */ + case InvalidTxid(errorDetails: String + ) + /** + * A valid transaction ID was not found in the wallet + */ + case TransactionNotFound(errorDetails: String + ) + /** + * Watcher lifecycle or subscription error + */ + case WatcherError(errorDetails: String + ) } -#if compiler(>=6) -extension TxInput: Sendable {} + +#if swift(>=5.8) +@_documentation(visibility: private) #endif +public struct FfiConverterTypeAccountInfoError: FfiConverterRustBuffer { + typealias SwiftType = AccountInfoError + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoError { + let variant: Int32 = try readInt(&buf) + switch variant { -extension TxInput: Equatable, Hashable { - public static func ==(lhs: TxInput, rhs: TxInput) -> Bool { - if lhs.txid != rhs.txid { - return false - } - if lhs.vout != rhs.vout { - return false - } - if lhs.scriptsig != rhs.scriptsig { - return false - } - if lhs.witness != rhs.witness { - return false - } - if lhs.sequence != rhs.sequence { - return false - } - return true - } + + + + case 1: return .InvalidExtendedKey( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAddress( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .ElectrumError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .WalletError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .SyncError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .UnsupportedKeyType( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .NetworkMismatch( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .InvalidTxid( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .TransactionNotFound( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 10: return .WatcherError( + errorDetails: try FfiConverterString.read(from: &buf) + ) - public func hash(into hasher: inout Hasher) { - hasher.combine(txid) - hasher.combine(vout) - hasher.combine(scriptsig) - hasher.combine(witness) - hasher.combine(sequence) + default: throw UniffiInternalError.unexpectedEnumCase + } } -} - -extension TxInput: Codable {} - + public static func write(_ value: AccountInfoError, into buf: inout [UInt8]) { + switch value { -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeTxInput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxInput { - return - try TxInput( - txid: FfiConverterString.read(from: &buf), - vout: FfiConverterUInt32.read(from: &buf), - scriptsig: FfiConverterString.read(from: &buf), - witness: FfiConverterSequenceString.read(from: &buf), - sequence: FfiConverterUInt32.read(from: &buf) - ) - } + - public static func write(_ value: TxInput, into buf: inout [UInt8]) { - FfiConverterString.write(value.txid, into: &buf) - FfiConverterUInt32.write(value.vout, into: &buf) - FfiConverterString.write(value.scriptsig, into: &buf) - FfiConverterSequenceString.write(value.witness, into: &buf) - FfiConverterUInt32.write(value.sequence, into: &buf) + + + case let .InvalidExtendedKey(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidAddress(errorDetails): + writeInt(&buf, Int32(2)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ElectrumError(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .WalletError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SyncError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .UnsupportedKeyType(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .NetworkMismatch(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidTxid(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .TransactionNotFound(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .WatcherError(errorDetails): + writeInt(&buf, Int32(10)) + FfiConverterString.write(errorDetails, into: &buf) + + } } } @@ -15415,128 +17653,109 @@ public struct FfiConverterTypeTxInput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxInput_lift(_ buf: RustBuffer) throws -> TxInput { - return try FfiConverterTypeTxInput.lift(buf) +public func FfiConverterTypeAccountInfoError_lift(_ buf: RustBuffer) throws -> AccountInfoError { + return try FfiConverterTypeAccountInfoError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxInput_lower(_ value: TxInput) -> RustBuffer { - return FfiConverterTypeTxInput.lower(value) +public func FfiConverterTypeAccountInfoError_lower(_ value: AccountInfoError) -> RustBuffer { + return FfiConverterTypeAccountInfoError.lower(value) +} + + +extension AccountInfoError: Equatable, Hashable {} + +extension AccountInfoError: Codable {} + + + + +extension AccountInfoError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } } + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Details about a transaction output. + * Account type classification for extended public keys. + * + * Determines the BIP standard, derivation path purpose, and script type. */ -public struct TxOutput { - /** - * The script public key (hex-encoded). - */ - public var scriptpubkey: String + +public enum AccountType { + /** - * The script public key type (e.g., "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"). + * BIP44 legacy (P2PKH) — xpub/tpub prefix */ - public var scriptpubkeyType: String? + case legacy /** - * The address corresponding to this script (if decodable). + * BIP49 wrapped segwit (P2SH-P2WPKH) — ypub/upub prefix */ - public var scriptpubkeyAddress: String? + case wrappedSegwit /** - * The value in satoshis. + * BIP84 native segwit (P2WPKH) — zpub/vpub prefix */ - public var value: Int64 + case nativeSegwit /** - * The output index in the transaction. + * BIP86 taproot (P2TR) */ - public var n: UInt32 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * The script public key (hex-encoded). - */scriptpubkey: String, - /** - * The script public key type (e.g., "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"). - */scriptpubkeyType: String?, - /** - * The address corresponding to this script (if decodable). - */scriptpubkeyAddress: String?, - /** - * The value in satoshis. - */value: Int64, - /** - * The output index in the transaction. - */n: UInt32) { - self.scriptpubkey = scriptpubkey - self.scriptpubkeyType = scriptpubkeyType - self.scriptpubkeyAddress = scriptpubkeyAddress - self.value = value - self.n = n - } + case taproot } + #if compiler(>=6) -extension TxOutput: Sendable {} +extension AccountType: Sendable {} #endif - -extension TxOutput: Equatable, Hashable { - public static func ==(lhs: TxOutput, rhs: TxOutput) -> Bool { - if lhs.scriptpubkey != rhs.scriptpubkey { - return false - } - if lhs.scriptpubkeyType != rhs.scriptpubkeyType { - return false - } - if lhs.scriptpubkeyAddress != rhs.scriptpubkeyAddress { - return false - } - if lhs.value != rhs.value { - return false - } - if lhs.n != rhs.n { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(scriptpubkey) - hasher.combine(scriptpubkeyType) - hasher.combine(scriptpubkeyAddress) - hasher.combine(value) - hasher.combine(n) - } -} - -extension TxOutput: Codable {} - - - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTxOutput: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxOutput { - return - try TxOutput( - scriptpubkey: FfiConverterString.read(from: &buf), - scriptpubkeyType: FfiConverterOptionString.read(from: &buf), - scriptpubkeyAddress: FfiConverterOptionString.read(from: &buf), - value: FfiConverterInt64.read(from: &buf), - n: FfiConverterUInt32.read(from: &buf) - ) +public struct FfiConverterTypeAccountType: FfiConverterRustBuffer { + typealias SwiftType = AccountType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .legacy + + case 2: return .wrappedSegwit + + case 3: return .nativeSegwit + + case 4: return .taproot + + default: throw UniffiInternalError.unexpectedEnumCase + } } - public static func write(_ value: TxOutput, into buf: inout [UInt8]) { - FfiConverterString.write(value.scriptpubkey, into: &buf) - FfiConverterOptionString.write(value.scriptpubkeyType, into: &buf) - FfiConverterOptionString.write(value.scriptpubkeyAddress, into: &buf) - FfiConverterInt64.write(value.value, into: &buf) - FfiConverterUInt32.write(value.n, into: &buf) + public static func write(_ value: AccountType, into buf: inout [UInt8]) { + switch value { + + + case .legacy: + writeInt(&buf, Int32(1)) + + + case .wrappedSegwit: + writeInt(&buf, Int32(2)) + + + case .nativeSegwit: + writeInt(&buf, Int32(3)) + + + case .taproot: + writeInt(&buf, Int32(4)) + + } } } @@ -15544,100 +17763,77 @@ public struct FfiConverterTypeTxOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxOutput_lift(_ buf: RustBuffer) throws -> TxOutput { - return try FfiConverterTypeTxOutput.lift(buf) +public func FfiConverterTypeAccountType_lift(_ buf: RustBuffer) throws -> AccountType { + return try FfiConverterTypeAccountType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxOutput_lower(_ value: TxOutput) -> RustBuffer { - return FfiConverterTypeTxOutput.lower(value) +public func FfiConverterTypeAccountType_lower(_ value: AccountType) -> RustBuffer { + return FfiConverterTypeAccountType.lower(value) } -/** - * Current state after accepting a scanned UR frame. - */ -public struct UrDecoderStatus { - /** - * Estimated completion from 0.0 through 1.0. - */ - public var progress: Double - /** - * Fountain source-fragment count, or 1 for a single-part UR. - */ - public var fragmentCount: UInt32 - /** - * Present once the complete message has been decoded. - */ - public var payload: UrPayload? +extension AccountType: Equatable, Hashable {} - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * Estimated completion from 0.0 through 1.0. - */progress: Double, - /** - * Fountain source-fragment count, or 1 for a single-part UR. - */fragmentCount: UInt32, - /** - * Present once the complete message has been decoded. - */payload: UrPayload?) { - self.progress = progress - self.fragmentCount = fragmentCount - self.payload = payload - } -} +extension AccountType: Codable {} -#if compiler(>=6) -extension UrDecoderStatus: Sendable {} -#endif -extension UrDecoderStatus: Equatable, Hashable { - public static func ==(lhs: UrDecoderStatus, rhs: UrDecoderStatus) -> Bool { - if lhs.progress != rhs.progress { - return false - } - if lhs.fragmentCount != rhs.fragmentCount { - return false - } - if lhs.payload != rhs.payload { - return false - } - return true - } - public func hash(into hasher: inout Hasher) { - hasher.combine(progress) - hasher.combine(fragmentCount) - hasher.combine(payload) - } -} -extension UrDecoderStatus: Codable {} + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum Activity { + + case onchain(OnchainActivity + ) + case lightning(LightningActivity + ) +} +#if compiler(>=6) +extension Activity: Sendable {} +#endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrDecoderStatus: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrDecoderStatus { - return - try UrDecoderStatus( - progress: FfiConverterDouble.read(from: &buf), - fragmentCount: FfiConverterUInt32.read(from: &buf), - payload: FfiConverterOptionTypeUrPayload.read(from: &buf) +public struct FfiConverterTypeActivity: FfiConverterRustBuffer { + typealias SwiftType = Activity + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Activity { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .onchain(try FfiConverterTypeOnchainActivity.read(from: &buf) ) + + case 2: return .lightning(try FfiConverterTypeLightningActivity.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } } - public static func write(_ value: UrDecoderStatus, into buf: inout [UInt8]) { - FfiConverterDouble.write(value.progress, into: &buf) - FfiConverterUInt32.write(value.fragmentCount, into: &buf) - FfiConverterOptionTypeUrPayload.write(value.payload, into: &buf) + public static func write(_ value: Activity, into buf: inout [UInt8]) { + switch value { + + + case let .onchain(v1): + writeInt(&buf, Int32(1)) + FfiConverterTypeOnchainActivity.write(v1, into: &buf) + + + case let .lightning(v1): + writeInt(&buf, Int32(2)) + FfiConverterTypeLightningActivity.write(v1, into: &buf) + + } } } @@ -15645,79 +17841,130 @@ public struct FfiConverterTypeUrDecoderStatus: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrDecoderStatus_lift(_ buf: RustBuffer) throws -> UrDecoderStatus { - return try FfiConverterTypeUrDecoderStatus.lift(buf) +public func FfiConverterTypeActivity_lift(_ buf: RustBuffer) throws -> Activity { + return try FfiConverterTypeActivity.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrDecoderStatus_lower(_ value: UrDecoderStatus) -> RustBuffer { - return FfiConverterTypeUrDecoderStatus.lower(value) +public func FfiConverterTypeActivity_lower(_ value: Activity) -> RustBuffer { + return FfiConverterTypeActivity.lower(value) } -public struct ValidationResult { - public var address: String - public var network: NetworkType - public var addressType: AddressType +extension Activity: Equatable, Hashable {} - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(address: String, network: NetworkType, addressType: AddressType) { - self.address = address - self.network = network - self.addressType = addressType - } +extension Activity: Codable {} + + + + + + + +public enum ActivityError: Swift.Error { + + + + case InvalidActivity(errorDetails: String + ) + case InitializationError(errorDetails: String + ) + case InsertError(errorDetails: String + ) + case RetrievalError(errorDetails: String + ) + case DataError(errorDetails: String + ) + case ConnectionError(errorDetails: String + ) + case SerializationError(errorDetails: String + ) } -#if compiler(>=6) -extension ValidationResult: Sendable {} + +#if swift(>=5.8) +@_documentation(visibility: private) #endif +public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { + typealias SwiftType = ActivityError + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityError { + let variant: Int32 = try readInt(&buf) + switch variant { -extension ValidationResult: Equatable, Hashable { - public static func ==(lhs: ValidationResult, rhs: ValidationResult) -> Bool { - if lhs.address != rhs.address { - return false - } - if lhs.network != rhs.network { - return false + + + + case 1: return .InvalidActivity( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .InsertError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .RetrievalError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .DataError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } - if lhs.addressType != rhs.addressType { - return false + } + + public static func write(_ value: ActivityError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidActivity(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InitializationError(errorDetails): + writeInt(&buf, Int32(2)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InsertError(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .RetrievalError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DataError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ConnectionError(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(address) - hasher.combine(network) - hasher.combine(addressType) - } -} - -extension ValidationResult: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeValidationResult: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ValidationResult { - return - try ValidationResult( - address: FfiConverterString.read(from: &buf), - network: FfiConverterTypeNetworkType.read(from: &buf), - addressType: FfiConverterTypeAddressType.read(from: &buf) - ) - } - - public static func write(_ value: ValidationResult, into buf: inout [UInt8]) { - FfiConverterString.write(value.address, into: &buf) - FfiConverterTypeNetworkType.write(value.network, into: &buf) - FfiConverterTypeAddressType.write(value.addressType, into: &buf) } } @@ -15725,271 +17972,157 @@ public struct FfiConverterTypeValidationResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeValidationResult_lift(_ buf: RustBuffer) throws -> ValidationResult { - return try FfiConverterTypeValidationResult.lift(buf) +public func FfiConverterTypeActivityError_lift(_ buf: RustBuffer) throws -> ActivityError { + return try FfiConverterTypeActivityError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeValidationResult_lower(_ value: ValidationResult) -> RustBuffer { - return FfiConverterTypeValidationResult.lower(value) +public func FfiConverterTypeActivityError_lower(_ value: ActivityError) -> RustBuffer { + return FfiConverterTypeActivityError.lower(value) } -/** - * Balance breakdown from BDK. - */ -public struct WalletBalance { - /** - * Confirmed and spendable balance (sats) - */ - public var confirmed: UInt64 - /** - * Immature coinbase outputs (sats) - */ - public var immature: UInt64 - /** - * Unconfirmed UTXOs from trusted sources (own change) (sats) - */ - public var trustedPending: UInt64 - /** - * Unconfirmed UTXOs from external sources (sats) - */ - public var untrustedPending: UInt64 - /** - * Total spendable: confirmed + trusted_pending (sats) - */ - public var spendable: UInt64 - /** - * Grand total: all categories (sats) - */ - public var total: UInt64 +extension ActivityError: Equatable, Hashable {} - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * Confirmed and spendable balance (sats) - */confirmed: UInt64, - /** - * Immature coinbase outputs (sats) - */immature: UInt64, - /** - * Unconfirmed UTXOs from trusted sources (own change) (sats) - */trustedPending: UInt64, - /** - * Unconfirmed UTXOs from external sources (sats) - */untrustedPending: UInt64, - /** - * Total spendable: confirmed + trusted_pending (sats) - */spendable: UInt64, - /** - * Grand total: all categories (sats) - */total: UInt64) { - self.confirmed = confirmed - self.immature = immature - self.trustedPending = trustedPending - self.untrustedPending = untrustedPending - self.spendable = spendable - self.total = total - } -} +extension ActivityError: Codable {} -#if compiler(>=6) -extension WalletBalance: Sendable {} -#endif -extension WalletBalance: Equatable, Hashable { - public static func ==(lhs: WalletBalance, rhs: WalletBalance) -> Bool { - if lhs.confirmed != rhs.confirmed { - return false - } - if lhs.immature != rhs.immature { - return false - } - if lhs.trustedPending != rhs.trustedPending { - return false - } - if lhs.untrustedPending != rhs.untrustedPending { - return false - } - if lhs.spendable != rhs.spendable { - return false - } - if lhs.total != rhs.total { - return false - } - return true - } - public func hash(into hasher: inout Hasher) { - hasher.combine(confirmed) - hasher.combine(immature) - hasher.combine(trustedPending) - hasher.combine(untrustedPending) - hasher.combine(spendable) - hasher.combine(total) +extension ActivityError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) } } -extension WalletBalance: Codable {} -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeWalletBalance: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletBalance { - return - try WalletBalance( - confirmed: FfiConverterUInt64.read(from: &buf), - immature: FfiConverterUInt64.read(from: &buf), - trustedPending: FfiConverterUInt64.read(from: &buf), - untrustedPending: FfiConverterUInt64.read(from: &buf), - spendable: FfiConverterUInt64.read(from: &buf), - total: FfiConverterUInt64.read(from: &buf) - ) - } +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - public static func write(_ value: WalletBalance, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.confirmed, into: &buf) - FfiConverterUInt64.write(value.immature, into: &buf) - FfiConverterUInt64.write(value.trustedPending, into: &buf) - FfiConverterUInt64.write(value.untrustedPending, into: &buf) - FfiConverterUInt64.write(value.spendable, into: &buf) - FfiConverterUInt64.write(value.total, into: &buf) - } +public enum ActivityFilter { + + case all + case lightning + case onchain } -#if swift(>=5.8) -@_documentation(visibility: private) +#if compiler(>=6) +extension ActivityFilter: Sendable {} #endif -public func FfiConverterTypeWalletBalance_lift(_ buf: RustBuffer) throws -> WalletBalance { - return try FfiConverterTypeWalletBalance.lift(buf) -} #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletBalance_lower(_ value: WalletBalance) -> RustBuffer { - return FfiConverterTypeWalletBalance.lower(value) -} - +public struct FfiConverterTypeActivityFilter: FfiConverterRustBuffer { + typealias SwiftType = ActivityFilter -/** - * Common parameters for creating and syncing a watch-only BDK wallet. - */ -public struct WalletParams { - /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) - */ - public var extendedKey: String - /** - * Electrum server URL for wallet sync - */ - public var electrumUrl: String - /** - * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. - */ - public var fingerprint: String? - /** - * Bitcoin network (auto-detected from key prefix if not specified) - */ - public var network: Network? - /** - * Override account type for ambiguous key prefixes (xpub/tpub) - */ - public var accountType: AccountType? + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityFilter { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .all + + case 2: return .lightning + + case 3: return .onchain + + default: throw UniffiInternalError.unexpectedEnumCase + } + } - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) - */extendedKey: String, - /** - * Electrum server URL for wallet sync - */electrumUrl: String, - /** - * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. - */fingerprint: String?, - /** - * Bitcoin network (auto-detected from key prefix if not specified) - */network: Network?, - /** - * Override account type for ambiguous key prefixes (xpub/tpub) - */accountType: AccountType?) { - self.extendedKey = extendedKey - self.electrumUrl = electrumUrl - self.fingerprint = fingerprint - self.network = network - self.accountType = accountType + public static func write(_ value: ActivityFilter, into buf: inout [UInt8]) { + switch value { + + + case .all: + writeInt(&buf, Int32(1)) + + + case .lightning: + writeInt(&buf, Int32(2)) + + + case .onchain: + writeInt(&buf, Int32(3)) + + } } } -#if compiler(>=6) -extension WalletParams: Sendable {} + +#if swift(>=5.8) +@_documentation(visibility: private) #endif +public func FfiConverterTypeActivityFilter_lift(_ buf: RustBuffer) throws -> ActivityFilter { + return try FfiConverterTypeActivityFilter.lift(buf) +} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityFilter_lower(_ value: ActivityFilter) -> RustBuffer { + return FfiConverterTypeActivityFilter.lower(value) +} -extension WalletParams: Equatable, Hashable { - public static func ==(lhs: WalletParams, rhs: WalletParams) -> Bool { - if lhs.extendedKey != rhs.extendedKey { - return false - } - if lhs.electrumUrl != rhs.electrumUrl { - return false - } - if lhs.fingerprint != rhs.fingerprint { - return false - } - if lhs.network != rhs.network { - return false - } - if lhs.accountType != rhs.accountType { - return false - } - return true - } - public func hash(into hasher: inout Hasher) { - hasher.combine(extendedKey) - hasher.combine(electrumUrl) - hasher.combine(fingerprint) - hasher.combine(network) - hasher.combine(accountType) - } -} +extension ActivityFilter: Equatable, Hashable {} + +extension ActivityFilter: Codable {} -extension WalletParams: Codable {} + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ActivityType { + + case onchain + case lightning +} + + +#if compiler(>=6) +extension ActivityType: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletParams { - return - try WalletParams( - extendedKey: FfiConverterString.read(from: &buf), - electrumUrl: FfiConverterString.read(from: &buf), - fingerprint: FfiConverterOptionString.read(from: &buf), - network: FfiConverterOptionTypeNetwork.read(from: &buf), - accountType: FfiConverterOptionTypeAccountType.read(from: &buf) - ) +public struct FfiConverterTypeActivityType: FfiConverterRustBuffer { + typealias SwiftType = ActivityType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .onchain + + case 2: return .lightning + + default: throw UniffiInternalError.unexpectedEnumCase + } } - public static func write(_ value: WalletParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.extendedKey, into: &buf) - FfiConverterString.write(value.electrumUrl, into: &buf) - FfiConverterOptionString.write(value.fingerprint, into: &buf) - FfiConverterOptionTypeNetwork.write(value.network, into: &buf) - FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) + public static func write(_ value: ActivityType, into buf: inout [UInt8]) { + switch value { + + + case .onchain: + writeInt(&buf, Int32(1)) + + + case .lightning: + writeInt(&buf, Int32(2)) + + } } } @@ -15997,160 +18130,96 @@ public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletParams_lift(_ buf: RustBuffer) throws -> WalletParams { - return try FfiConverterTypeWalletParams.lift(buf) +public func FfiConverterTypeActivityType_lift(_ buf: RustBuffer) throws -> ActivityType { + return try FfiConverterTypeActivityType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletParams_lower(_ value: WalletParams) -> RustBuffer { - return FfiConverterTypeWalletParams.lower(value) +public func FfiConverterTypeActivityType_lower(_ value: ActivityType) -> RustBuffer { + return FfiConverterTypeActivityType.lower(value) } -/** - * Parameters for starting an xpub transaction watcher. - */ -public struct WatcherParams { - /** - * Caller-supplied identifier for this watcher. - */ - public var watcherId: String - /** - * Wallet id that scopes the activities this watcher emits. Apps may use - * one wallet id for several account watchers and merge their snapshots. - */ - public var walletId: String - /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). - */ - public var extendedKey: String - /** - * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). - */ - public var electrumUrl: String - /** - * Bitcoin network override (auto-detected from key prefix if None). - */ - public var network: Network? - /** - * Account type override (auto-detected from key prefix if None). - */ - public var accountType: AccountType? - /** - * Number of unused addresses to monitor beyond the last used - * (defaults to `DEFAULT_GAP_LIMIT` when None). - */ - public var gapLimit: UInt32? +extension ActivityType: Equatable, Hashable {} - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init( - /** - * Caller-supplied identifier for this watcher. - */watcherId: String, - /** - * Wallet id that scopes the activities this watcher emits. Apps may use - * one wallet id for several account watchers and merge their snapshots. - */walletId: String, - /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). - */extendedKey: String, - /** - * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). - */electrumUrl: String, - /** - * Bitcoin network override (auto-detected from key prefix if None). - */network: Network?, - /** - * Account type override (auto-detected from key prefix if None). - */accountType: AccountType?, - /** - * Number of unused addresses to monitor beyond the last used - * (defaults to `DEFAULT_GAP_LIMIT` when None). - */gapLimit: UInt32?) { - self.watcherId = watcherId - self.walletId = walletId - self.extendedKey = extendedKey - self.electrumUrl = electrumUrl - self.network = network - self.accountType = accountType - self.gapLimit = gapLimit - } -} +extension ActivityType: Codable {} -#if compiler(>=6) -extension WatcherParams: Sendable {} -#endif -extension WatcherParams: Equatable, Hashable { - public static func ==(lhs: WatcherParams, rhs: WatcherParams) -> Bool { - if lhs.watcherId != rhs.watcherId { - return false - } - if lhs.walletId != rhs.walletId { - return false - } - if lhs.extendedKey != rhs.extendedKey { - return false - } - if lhs.electrumUrl != rhs.electrumUrl { - return false - } - if lhs.network != rhs.network { - return false - } - if lhs.accountType != rhs.accountType { - return false - } - if lhs.gapLimit != rhs.gapLimit { - return false - } - return true - } - public func hash(into hasher: inout Hasher) { - hasher.combine(watcherId) - hasher.combine(walletId) - hasher.combine(extendedKey) - hasher.combine(electrumUrl) - hasher.combine(network) - hasher.combine(accountType) - hasher.combine(gapLimit) - } + + + +public enum AddressError: Swift.Error { + + + + case InvalidAddress + case InvalidNetwork + case MnemonicGenerationFailed + case InvalidMnemonic + case InvalidEntropy + case AddressDerivationFailed } -extension WatcherParams: Codable {} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { + typealias SwiftType = AddressError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidAddress + case 2: return .InvalidNetwork + case 3: return .MnemonicGenerationFailed + case 4: return .InvalidMnemonic + case 5: return .InvalidEntropy + case 6: return .AddressDerivationFailed + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + public static func write(_ value: AddressError, into buf: inout [UInt8]) { + switch value { -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WatcherParams { - return - try WatcherParams( - watcherId: FfiConverterString.read(from: &buf), - walletId: FfiConverterString.read(from: &buf), - extendedKey: FfiConverterString.read(from: &buf), - electrumUrl: FfiConverterString.read(from: &buf), - network: FfiConverterOptionTypeNetwork.read(from: &buf), - accountType: FfiConverterOptionTypeAccountType.read(from: &buf), - gapLimit: FfiConverterOptionUInt32.read(from: &buf) - ) - } + - public static func write(_ value: WatcherParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.watcherId, into: &buf) - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.extendedKey, into: &buf) - FfiConverterString.write(value.electrumUrl, into: &buf) - FfiConverterOptionTypeNetwork.write(value.network, into: &buf) - FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) - FfiConverterOptionUInt32.write(value.gapLimit, into: &buf) + + + case .InvalidAddress: + writeInt(&buf, Int32(1)) + + + case .InvalidNetwork: + writeInt(&buf, Int32(2)) + + + case .MnemonicGenerationFailed: + writeInt(&buf, Int32(3)) + + + case .InvalidMnemonic: + writeInt(&buf, Int32(4)) + + + case .InvalidEntropy: + writeInt(&buf, Int32(5)) + + + case .AddressDerivationFailed: + writeInt(&buf, Int32(6)) + + } } } @@ -16158,182 +18227,105 @@ public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWatcherParams_lift(_ buf: RustBuffer) throws -> WatcherParams { - return try FfiConverterTypeWatcherParams.lift(buf) +public func FfiConverterTypeAddressError_lift(_ buf: RustBuffer) throws -> AddressError { + return try FfiConverterTypeAddressError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWatcherParams_lower(_ value: WatcherParams) -> RustBuffer { - return FfiConverterTypeWatcherParams.lower(value) +public func FfiConverterTypeAddressError_lower(_ value: AddressError) -> RustBuffer { + return FfiConverterTypeAddressError.lower(value) } -/** - * Errors specific to account info operations (BDK/Electrum-based). - */ -public enum AccountInfoError: Swift.Error { +extension AddressError: Equatable, Hashable {} +extension AddressError: Codable {} + + + + +extension AddressError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum AddressType { - - /** - * The provided extended public key is invalid or cannot be parsed - */ - case InvalidExtendedKey(errorDetails: String - ) - /** - * The provided address is invalid - */ - case InvalidAddress(errorDetails: String - ) - /** - * Electrum connection or query failed - */ - case ElectrumError(errorDetails: String - ) - /** - * BDK wallet creation or operation error - */ - case WalletError(errorDetails: String - ) - /** - * Wallet sync with Electrum failed - */ - case SyncError(errorDetails: String - ) - /** - * The key type/prefix is not recognized - */ - case UnsupportedKeyType(errorDetails: String - ) - /** - * Network mismatch between key prefix and specified network - */ - case NetworkMismatch(errorDetails: String - ) - /** - * Invalid transaction ID provided - */ - case InvalidTxid(errorDetails: String - ) - /** - * A valid transaction ID was not found in the wallet - */ - case TransactionNotFound(errorDetails: String - ) - /** - * Watcher lifecycle or subscription error - */ - case WatcherError(errorDetails: String - ) + case p2pkh + case p2sh + case p2wpkh + case p2wsh + case p2tr + case unknown } +#if compiler(>=6) +extension AddressType: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAccountInfoError: FfiConverterRustBuffer { - typealias SwiftType = AccountInfoError +public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { + typealias SwiftType = AddressType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressType { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .p2pkh - case 1: return .InvalidExtendedKey( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidAddress( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .ElectrumError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .WalletError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .SyncError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .UnsupportedKeyType( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .NetworkMismatch( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .InvalidTxid( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .TransactionNotFound( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 10: return .WatcherError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .p2sh + + case 3: return .p2wpkh + + case 4: return .p2wsh + + case 5: return .p2tr + + case 6: return .unknown + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AccountInfoError, into buf: inout [UInt8]) { + public static func write(_ value: AddressType, into buf: inout [UInt8]) { switch value { - - - - case let .InvalidExtendedKey(errorDetails): + case .p2pkh: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .InvalidAddress(errorDetails): + + case .p2sh: writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .ElectrumError(errorDetails): + + case .p2wpkh: writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .WalletError(errorDetails): - writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .SyncError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - + case .p2wsh: + writeInt(&buf, Int32(4)) - case let .UnsupportedKeyType(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .NetworkMismatch(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - + case .p2tr: + writeInt(&buf, Int32(5)) - case let .InvalidTxid(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .TransactionNotFound(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - + case .unknown: + writeInt(&buf, Int32(6)) - case let .WatcherError(errorDetails): - writeInt(&buf, Int32(10)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -16342,106 +18334,82 @@ public struct FfiConverterTypeAccountInfoError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoError_lift(_ buf: RustBuffer) throws -> AccountInfoError { - return try FfiConverterTypeAccountInfoError.lift(buf) +public func FfiConverterTypeAddressType_lift(_ buf: RustBuffer) throws -> AddressType { + return try FfiConverterTypeAddressType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoError_lower(_ value: AccountInfoError) -> RustBuffer { - return FfiConverterTypeAccountInfoError.lower(value) +public func FfiConverterTypeAddressType_lower(_ value: AddressType) -> RustBuffer { + return FfiConverterTypeAddressType.lower(value) } -extension AccountInfoError: Equatable, Hashable {} - -extension AccountInfoError: Codable {} - +extension AddressType: Equatable, Hashable {} +extension AddressType: Codable {} -extension AccountInfoError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Account type classification for extended public keys. - * - * Determines the BIP standard, derivation path purpose, and script type. - */ -public enum AccountType { +public enum BitcoinNetworkEnum { - /** - * BIP44 legacy (P2PKH) — xpub/tpub prefix - */ - case legacy - /** - * BIP49 wrapped segwit (P2SH-P2WPKH) — ypub/upub prefix - */ - case wrappedSegwit - /** - * BIP84 native segwit (P2WPKH) — zpub/vpub prefix - */ - case nativeSegwit - /** - * BIP86 taproot (P2TR) - */ - case taproot + case mainnet + case testnet + case signet + case regtest } #if compiler(>=6) -extension AccountType: Sendable {} +extension BitcoinNetworkEnum: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAccountType: FfiConverterRustBuffer { - typealias SwiftType = AccountType +public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { + typealias SwiftType = BitcoinNetworkEnum - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BitcoinNetworkEnum { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .legacy + case 1: return .mainnet - case 2: return .wrappedSegwit + case 2: return .testnet - case 3: return .nativeSegwit + case 3: return .signet - case 4: return .taproot + case 4: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AccountType, into buf: inout [UInt8]) { + public static func write(_ value: BitcoinNetworkEnum, into buf: inout [UInt8]) { switch value { - case .legacy: + case .mainnet: writeInt(&buf, Int32(1)) - case .wrappedSegwit: + case .testnet: writeInt(&buf, Int32(2)) - case .nativeSegwit: + case .signet: writeInt(&buf, Int32(3)) - case .taproot: + case .regtest: writeInt(&buf, Int32(4)) } @@ -16452,75 +18420,190 @@ public struct FfiConverterTypeAccountType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountType_lift(_ buf: RustBuffer) throws -> AccountType { - return try FfiConverterTypeAccountType.lift(buf) +public func FfiConverterTypeBitcoinNetworkEnum_lift(_ buf: RustBuffer) throws -> BitcoinNetworkEnum { + return try FfiConverterTypeBitcoinNetworkEnum.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountType_lower(_ value: AccountType) -> RustBuffer { - return FfiConverterTypeAccountType.lower(value) +public func FfiConverterTypeBitcoinNetworkEnum_lower(_ value: BitcoinNetworkEnum) -> RustBuffer { + return FfiConverterTypeBitcoinNetworkEnum.lower(value) } -extension AccountType: Equatable, Hashable {} +extension BitcoinNetworkEnum: Equatable, Hashable {} -extension AccountType: Codable {} +extension BitcoinNetworkEnum: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum Activity { +public enum BlocktankError: Swift.Error { + - case onchain(OnchainActivity + + case HttpClient(errorDetails: String ) - case lightning(LightningActivity + case BlocktankClient(errorDetails: String + ) + case InvalidBlocktank(errorDetails: String + ) + case InitializationError(errorDetails: String + ) + case InsertError(errorDetails: String + ) + case RetrievalError(errorDetails: String + ) + case DataError(errorDetails: String + ) + case ConnectionError(errorDetails: String + ) + case SerializationError(errorDetails: String + ) + case ChannelOpen(errorType: BtChannelOrderErrorType, errorDetails: String + ) + case OrderState(errorDetails: String + ) + case InvalidParameter(errorDetails: String + ) + case DatabaseError(errorDetails: String ) } -#if compiler(>=6) -extension Activity: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivity: FfiConverterRustBuffer { - typealias SwiftType = Activity +public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { + typealias SwiftType = BlocktankError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Activity { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BlocktankError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .onchain(try FfiConverterTypeOnchainActivity.read(from: &buf) - ) - - case 2: return .lightning(try FfiConverterTypeLightningActivity.read(from: &buf) - ) + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .HttpClient( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .BlocktankClient( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidBlocktank( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .InsertError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .RetrievalError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .DataError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 10: return .ChannelOpen( + errorType: try FfiConverterTypeBtChannelOrderErrorType.read(from: &buf), + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 11: return .OrderState( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 12: return .InvalidParameter( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 13: return .DatabaseError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Activity, into buf: inout [UInt8]) { + public static func write(_ value: BlocktankError, into buf: inout [UInt8]) { switch value { + + + - case let .onchain(v1): + case let .HttpClient(errorDetails): writeInt(&buf, Int32(1)) - FfiConverterTypeOnchainActivity.write(v1, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lightning(v1): + case let .BlocktankClient(errorDetails): writeInt(&buf, Int32(2)) - FfiConverterTypeLightningActivity.write(v1, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidBlocktank(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InitializationError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InsertError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .RetrievalError(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DataError(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ConnectionError(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ChannelOpen(errorType,errorDetails): + writeInt(&buf, Int32(10)) + FfiConverterTypeBtChannelOrderErrorType.write(errorType, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .OrderState(errorDetails): + writeInt(&buf, Int32(11)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidParameter(errorDetails): + writeInt(&buf, Int32(12)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DatabaseError(errorDetails): + writeInt(&buf, Int32(13)) + FfiConverterString.write(errorDetails, into: &buf) } } @@ -16530,81 +18613,101 @@ public struct FfiConverterTypeActivity: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivity_lift(_ buf: RustBuffer) throws -> Activity { - return try FfiConverterTypeActivity.lift(buf) +public func FfiConverterTypeBlocktankError_lift(_ buf: RustBuffer) throws -> BlocktankError { + return try FfiConverterTypeBlocktankError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivity_lower(_ value: Activity) -> RustBuffer { - return FfiConverterTypeActivity.lower(value) +public func FfiConverterTypeBlocktankError_lower(_ value: BlocktankError) -> RustBuffer { + return FfiConverterTypeBlocktankError.lower(value) } -extension Activity: Equatable, Hashable {} +extension BlocktankError: Equatable, Hashable {} + +extension BlocktankError: Codable {} -extension Activity: Codable {} +extension BlocktankError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + -public enum ActivityError: Swift.Error { +/** + * Errors surfaced by the Boltz swaps module. + */ +public enum BoltzError: Swift.Error { - case InvalidActivity(errorDetails: String - ) case InitializationError(errorDetails: String ) - case InsertError(errorDetails: String + case ConnectionError(errorDetails: String ) - case RetrievalError(errorDetails: String + case DatabaseError(errorDetails: String ) - case DataError(errorDetails: String + case ApiError(errorDetails: String ) - case ConnectionError(errorDetails: String + case SwapError(errorDetails: String + ) + case BroadcastError(errorDetails: String + ) + case InvalidInput(errorDetails: String ) case SerializationError(errorDetails: String ) + case NotFound(errorDetails: String + ) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { - typealias SwiftType = ActivityError +public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { + typealias SwiftType = BoltzError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidActivity( + case 1: return .InitializationError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 2: return .InitializationError( + case 2: return .ConnectionError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 3: return .InsertError( + case 3: return .DatabaseError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 4: return .RetrievalError( + case 4: return .ApiError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 5: return .DataError( + case 5: return .SwapError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 6: return .ConnectionError( + case 6: return .BroadcastError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 7: return .SerializationError( + case 7: return .InvalidInput( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .NotFound( errorDetails: try FfiConverterString.read(from: &buf) ) @@ -16612,47 +18715,57 @@ public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { } } - public static func write(_ value: ActivityError, into buf: inout [UInt8]) { + public static func write(_ value: BoltzError, into buf: inout [UInt8]) { switch value { - case let .InvalidActivity(errorDetails): + case let .InitializationError(errorDetails): writeInt(&buf, Int32(1)) FfiConverterString.write(errorDetails, into: &buf) - case let .InitializationError(errorDetails): + case let .ConnectionError(errorDetails): writeInt(&buf, Int32(2)) FfiConverterString.write(errorDetails, into: &buf) - case let .InsertError(errorDetails): + case let .DatabaseError(errorDetails): writeInt(&buf, Int32(3)) FfiConverterString.write(errorDetails, into: &buf) - case let .RetrievalError(errorDetails): + case let .ApiError(errorDetails): writeInt(&buf, Int32(4)) FfiConverterString.write(errorDetails, into: &buf) - case let .DataError(errorDetails): + case let .SwapError(errorDetails): writeInt(&buf, Int32(5)) FfiConverterString.write(errorDetails, into: &buf) - case let .ConnectionError(errorDetails): + case let .BroadcastError(errorDetails): writeInt(&buf, Int32(6)) FfiConverterString.write(errorDetails, into: &buf) - case let .SerializationError(errorDetails): + case let .InvalidInput(errorDetails): writeInt(&buf, Int32(7)) FfiConverterString.write(errorDetails, into: &buf) + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .NotFound(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -16661,26 +18774,26 @@ public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityError_lift(_ buf: RustBuffer) throws -> ActivityError { - return try FfiConverterTypeActivityError.lift(buf) +public func FfiConverterTypeBoltzError_lift(_ buf: RustBuffer) throws -> BoltzError { + return try FfiConverterTypeBoltzError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityError_lower(_ value: ActivityError) -> RustBuffer { - return FfiConverterTypeActivityError.lower(value) +public func FfiConverterTypeBoltzError_lower(_ value: BoltzError) -> RustBuffer { + return FfiConverterTypeBoltzError.lower(value) } -extension ActivityError: Equatable, Hashable {} +extension BoltzError: Equatable, Hashable {} -extension ActivityError: Codable {} +extension BoltzError: Codable {} -extension ActivityError: Foundation.LocalizedError { +extension BoltzError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -16691,223 +18804,58 @@ extension ActivityError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Bitcoin network selection for Boltz swaps. Maps to the networks Boltz + * operates on (mainnet, testnet, regtest). + */ -public enum ActivityFilter { - - case all - case lightning - case onchain -} - - -#if compiler(>=6) -extension ActivityFilter: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeActivityFilter: FfiConverterRustBuffer { - typealias SwiftType = ActivityFilter - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityFilter { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .all - - case 2: return .lightning - - case 3: return .onchain - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: ActivityFilter, into buf: inout [UInt8]) { - switch value { - - - case .all: - writeInt(&buf, Int32(1)) - - - case .lightning: - writeInt(&buf, Int32(2)) - - - case .onchain: - writeInt(&buf, Int32(3)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityFilter_lift(_ buf: RustBuffer) throws -> ActivityFilter { - return try FfiConverterTypeActivityFilter.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityFilter_lower(_ value: ActivityFilter) -> RustBuffer { - return FfiConverterTypeActivityFilter.lower(value) -} - - -extension ActivityFilter: Equatable, Hashable {} - -extension ActivityFilter: Codable {} - - - - - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum ActivityType { - - case onchain - case lightning -} - - -#if compiler(>=6) -extension ActivityType: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeActivityType: FfiConverterRustBuffer { - typealias SwiftType = ActivityType - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityType { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .onchain - - case 2: return .lightning - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: ActivityType, into buf: inout [UInt8]) { - switch value { - - - case .onchain: - writeInt(&buf, Int32(1)) - - - case .lightning: - writeInt(&buf, Int32(2)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityType_lift(_ buf: RustBuffer) throws -> ActivityType { - return try FfiConverterTypeActivityType.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityType_lower(_ value: ActivityType) -> RustBuffer { - return FfiConverterTypeActivityType.lower(value) -} - - -extension ActivityType: Equatable, Hashable {} - -extension ActivityType: Codable {} - - - - - - - -public enum AddressError: Swift.Error { - - +public enum BoltzNetwork { - case InvalidAddress - case InvalidNetwork - case MnemonicGenerationFailed - case InvalidMnemonic - case InvalidEntropy - case AddressDerivationFailed + case mainnet + case testnet + case regtest } +#if compiler(>=6) +extension BoltzNetwork: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { - typealias SwiftType = AddressError +public struct FfiConverterTypeBoltzNetwork: FfiConverterRustBuffer { + typealias SwiftType = BoltzNetwork - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzNetwork { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .mainnet - case 1: return .InvalidAddress - case 2: return .InvalidNetwork - case 3: return .MnemonicGenerationFailed - case 4: return .InvalidMnemonic - case 5: return .InvalidEntropy - case 6: return .AddressDerivationFailed - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .testnet + + case 3: return .regtest + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AddressError, into buf: inout [UInt8]) { + public static func write(_ value: BoltzNetwork, into buf: inout [UInt8]) { switch value { - - - - case .InvalidAddress: + case .mainnet: writeInt(&buf, Int32(1)) - case .InvalidNetwork: + case .testnet: writeInt(&buf, Int32(2)) - case .MnemonicGenerationFailed: + case .regtest: writeInt(&buf, Int32(3)) - - case .InvalidMnemonic: - writeInt(&buf, Int32(4)) - - - case .InvalidEntropy: - writeInt(&buf, Int32(5)) - - - case .AddressDerivationFailed: - writeInt(&buf, Int32(6)) - } } } @@ -16916,105 +18864,116 @@ public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressError_lift(_ buf: RustBuffer) throws -> AddressError { - return try FfiConverterTypeAddressError.lift(buf) +public func FfiConverterTypeBoltzNetwork_lift(_ buf: RustBuffer) throws -> BoltzNetwork { + return try FfiConverterTypeBoltzNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressError_lower(_ value: AddressError) -> RustBuffer { - return FfiConverterTypeAddressError.lower(value) +public func FfiConverterTypeBoltzNetwork_lower(_ value: BoltzNetwork) -> RustBuffer { + return FfiConverterTypeBoltzNetwork.lower(value) } -extension AddressError: Equatable, Hashable {} - -extension AddressError: Codable {} - +extension BoltzNetwork: Equatable, Hashable {} +extension BoltzNetwork: Codable {} -extension AddressError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Events emitted to a registered [`crate::modules::boltz::BoltzEventListener`] + * as swaps progress through their lifecycle. + */ -public enum AddressType { +public enum BoltzSwapEvent { - case p2pkh - case p2sh - case p2wpkh - case p2wsh - case p2tr - case unknown + /** + * The swap transitioned to a new status. + */ + case statusUpdate(swapId: String, status: BoltzSwapStatus + ) + /** + * A reverse swap was claimed onchain. `txid` is the claim transaction. + */ + case claimed(swapId: String, txid: String + ) + /** + * A submarine swap was refunded onchain. `txid` is the refund transaction. + */ + case refunded(swapId: String, txid: String + ) + /** + * An error occurred while processing the swap (e.g. an auto-claim failed). + */ + case error(swapId: String, message: String + ) } #if compiler(>=6) -extension AddressType: Sendable {} +extension BoltzSwapEvent: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { - typealias SwiftType = AddressType +public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapEvent - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapEvent { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .p2pkh - - case 2: return .p2sh - - case 3: return .p2wpkh + case 1: return .statusUpdate(swapId: try FfiConverterString.read(from: &buf), status: try FfiConverterTypeBoltzSwapStatus.read(from: &buf) + ) - case 4: return .p2wsh + case 2: return .claimed(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) + ) - case 5: return .p2tr + case 3: return .refunded(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) + ) - case 6: return .unknown + case 4: return .error(swapId: try FfiConverterString.read(from: &buf), message: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AddressType, into buf: inout [UInt8]) { + public static func write(_ value: BoltzSwapEvent, into buf: inout [UInt8]) { switch value { - case .p2pkh: + case let .statusUpdate(swapId,status): writeInt(&buf, Int32(1)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterTypeBoltzSwapStatus.write(status, into: &buf) + - - case .p2sh: + case let .claimed(swapId,txid): writeInt(&buf, Int32(2)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(txid, into: &buf) + - - case .p2wpkh: + case let .refunded(swapId,txid): writeInt(&buf, Int32(3)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(txid, into: &buf) + - - case .p2wsh: + case let .error(swapId,message): writeInt(&buf, Int32(4)) - - - case .p2tr: - writeInt(&buf, Int32(5)) - - - case .unknown: - writeInt(&buf, Int32(6)) - + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(message, into: &buf) + } } } @@ -17023,21 +18982,21 @@ public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressType_lift(_ buf: RustBuffer) throws -> AddressType { - return try FfiConverterTypeAddressType.lift(buf) +public func FfiConverterTypeBoltzSwapEvent_lift(_ buf: RustBuffer) throws -> BoltzSwapEvent { + return try FfiConverterTypeBoltzSwapEvent.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressType_lower(_ value: AddressType) -> RustBuffer { - return FfiConverterTypeAddressType.lower(value) +public func FfiConverterTypeBoltzSwapEvent_lower(_ value: BoltzSwapEvent) -> RustBuffer { + return FfiConverterTypeBoltzSwapEvent.lower(value) } -extension AddressType: Equatable, Hashable {} +extension BoltzSwapEvent: Equatable, Hashable {} -extension AddressType: Codable {} +extension BoltzSwapEvent: Codable {} @@ -17046,61 +19005,202 @@ extension AddressType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Typed view of the Boltz swap lifecycle. `Unknown` carries the raw status so + * new server-side states don't break the bindings. + * + * See . + */ -public enum BitcoinNetworkEnum { +public enum BoltzSwapStatus { - case mainnet - case testnet - case signet - case regtest + /** + * `swap.created` — initial state. + */ + case swapCreated + /** + * `invoice.set` — invoice attached to a submarine swap. + */ + case invoiceSet + /** + * `transaction.mempool` — a lockup transaction is in the mempool. + */ + case transactionMempool + /** + * `transaction.confirmed` — a lockup transaction confirmed. + */ + case transactionConfirmed + /** + * `invoice.pending` — Boltz is paying the submarine swap invoice. + */ + case invoicePending + /** + * `invoice.paid` — submarine swap invoice paid by Boltz. + */ + case invoicePaid + /** + * `invoice.settled` — reverse swap invoice settled (preimage revealed). + */ + case invoiceSettled + /** + * `invoice.failedToPay` — submarine swap invoice could not be paid; refund. + */ + case invoiceFailedToPay + /** + * `invoice.expired` — reverse swap invoice expired before payment. + */ + case invoiceExpired + /** + * `transaction.claim.pending` — Boltz ready for a cooperative claim. + */ + case transactionClaimPending + /** + * `transaction.claimed` — onchain funds claimed. + */ + case transactionClaimed + /** + * `transaction.refunded` — onchain funds refunded. + */ + case transactionRefunded + /** + * `transaction.lockupFailed` — wrong amount locked; can refund. + */ + case transactionLockupFailed + /** + * `transaction.failed` — Boltz failed to lock the agreed funds. + */ + case transactionFailed + /** + * `swap.expired` — swap expired without completing. + */ + case swapExpired + /** + * Any status not yet modelled. `raw` holds the verbatim Boltz status. + */ + case unknown(raw: String + ) } #if compiler(>=6) -extension BitcoinNetworkEnum: Sendable {} +extension BoltzSwapStatus: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { - typealias SwiftType = BitcoinNetworkEnum +public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapStatus - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BitcoinNetworkEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapStatus { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .mainnet + case 1: return .swapCreated - case 2: return .testnet + case 2: return .invoiceSet - case 3: return .signet + case 3: return .transactionMempool - case 4: return .regtest + case 4: return .transactionConfirmed + + case 5: return .invoicePending + + case 6: return .invoicePaid + + case 7: return .invoiceSettled + + case 8: return .invoiceFailedToPay + + case 9: return .invoiceExpired + + case 10: return .transactionClaimPending + + case 11: return .transactionClaimed + + case 12: return .transactionRefunded + + case 13: return .transactionLockupFailed + + case 14: return .transactionFailed + + case 15: return .swapExpired + + case 16: return .unknown(raw: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BitcoinNetworkEnum, into buf: inout [UInt8]) { + public static func write(_ value: BoltzSwapStatus, into buf: inout [UInt8]) { switch value { - case .mainnet: + case .swapCreated: writeInt(&buf, Int32(1)) - case .testnet: + case .invoiceSet: writeInt(&buf, Int32(2)) - case .signet: + case .transactionMempool: writeInt(&buf, Int32(3)) - case .regtest: + case .transactionConfirmed: writeInt(&buf, Int32(4)) + + case .invoicePending: + writeInt(&buf, Int32(5)) + + + case .invoicePaid: + writeInt(&buf, Int32(6)) + + + case .invoiceSettled: + writeInt(&buf, Int32(7)) + + + case .invoiceFailedToPay: + writeInt(&buf, Int32(8)) + + + case .invoiceExpired: + writeInt(&buf, Int32(9)) + + + case .transactionClaimPending: + writeInt(&buf, Int32(10)) + + + case .transactionClaimed: + writeInt(&buf, Int32(11)) + + + case .transactionRefunded: + writeInt(&buf, Int32(12)) + + + case .transactionLockupFailed: + writeInt(&buf, Int32(13)) + + + case .transactionFailed: + writeInt(&buf, Int32(14)) + + + case .swapExpired: + writeInt(&buf, Int32(15)) + + + case let .unknown(raw): + writeInt(&buf, Int32(16)) + FfiConverterString.write(raw, into: &buf) + } } } @@ -17109,191 +19209,78 @@ public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBitcoinNetworkEnum_lift(_ buf: RustBuffer) throws -> BitcoinNetworkEnum { - return try FfiConverterTypeBitcoinNetworkEnum.lift(buf) +public func FfiConverterTypeBoltzSwapStatus_lift(_ buf: RustBuffer) throws -> BoltzSwapStatus { + return try FfiConverterTypeBoltzSwapStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBitcoinNetworkEnum_lower(_ value: BitcoinNetworkEnum) -> RustBuffer { - return FfiConverterTypeBitcoinNetworkEnum.lower(value) +public func FfiConverterTypeBoltzSwapStatus_lower(_ value: BoltzSwapStatus) -> RustBuffer { + return FfiConverterTypeBoltzSwapStatus.lower(value) } -extension BitcoinNetworkEnum: Equatable, Hashable {} - -extension BitcoinNetworkEnum: Codable {} +extension BoltzSwapStatus: Equatable, Hashable {} +extension BoltzSwapStatus: Codable {} -public enum BlocktankError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The direction of a Boltz swap. + * + * - `Submarine`: onchain Bitcoin -> Lightning (the user locks onchain funds, + * Boltz pays a Lightning invoice). + * - `Reverse`: Lightning -> onchain Bitcoin (the user pays a Boltz hold + * invoice, Boltz locks onchain funds the user then claims). + */ +public enum BoltzSwapType { - - case HttpClient(errorDetails: String - ) - case BlocktankClient(errorDetails: String - ) - case InvalidBlocktank(errorDetails: String - ) - case InitializationError(errorDetails: String - ) - case InsertError(errorDetails: String - ) - case RetrievalError(errorDetails: String - ) - case DataError(errorDetails: String - ) - case ConnectionError(errorDetails: String - ) - case SerializationError(errorDetails: String - ) - case ChannelOpen(errorType: BtChannelOrderErrorType, errorDetails: String - ) - case OrderState(errorDetails: String - ) - case InvalidParameter(errorDetails: String - ) - case DatabaseError(errorDetails: String - ) + case submarine + case reverse } +#if compiler(>=6) +extension BoltzSwapType: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { - typealias SwiftType = BlocktankError +public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BlocktankError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapType { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .submarine - case 1: return .HttpClient( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .BlocktankClient( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .InvalidBlocktank( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .InsertError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .RetrievalError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .DataError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .SerializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 10: return .ChannelOpen( - errorType: try FfiConverterTypeBtChannelOrderErrorType.read(from: &buf), - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 11: return .OrderState( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 12: return .InvalidParameter( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 13: return .DatabaseError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .reverse + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BlocktankError, into buf: inout [UInt8]) { - switch value { - - - - - - case let .HttpClient(errorDetails): - writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .BlocktankClient(errorDetails): - writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InvalidBlocktank(errorDetails): - writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InitializationError(errorDetails): - writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InsertError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .RetrievalError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .DataError(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .SerializationError(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - + public static func write(_ value: BoltzSwapType, into buf: inout [UInt8]) { + switch value { - case let .ChannelOpen(errorType,errorDetails): - writeInt(&buf, Int32(10)) - FfiConverterTypeBtChannelOrderErrorType.write(errorType, into: &buf) - FfiConverterString.write(errorDetails, into: &buf) - - case let .OrderState(errorDetails): - writeInt(&buf, Int32(11)) - FfiConverterString.write(errorDetails, into: &buf) - + case .submarine: + writeInt(&buf, Int32(1)) - case let .InvalidParameter(errorDetails): - writeInt(&buf, Int32(12)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .DatabaseError(errorDetails): - writeInt(&buf, Int32(13)) - FfiConverterString.write(errorDetails, into: &buf) - + case .reverse: + writeInt(&buf, Int32(2)) + } } } @@ -17302,59 +19289,39 @@ public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBlocktankError_lift(_ buf: RustBuffer) throws -> BlocktankError { - return try FfiConverterTypeBlocktankError.lift(buf) +public func FfiConverterTypeBoltzSwapType_lift(_ buf: RustBuffer) throws -> BoltzSwapType { + return try FfiConverterTypeBoltzSwapType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBlocktankError_lower(_ value: BlocktankError) -> RustBuffer { - return FfiConverterTypeBlocktankError.lower(value) +public func FfiConverterTypeBoltzSwapType_lower(_ value: BoltzSwapType) -> RustBuffer { + return FfiConverterTypeBoltzSwapType.lower(value) } -extension BlocktankError: Equatable, Hashable {} - -extension BlocktankError: Codable {} - +extension BoltzSwapType: Equatable, Hashable {} +extension BoltzSwapType: Codable {} -extension BlocktankError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -/** - * Errors surfaced by the Boltz swaps module. - */ -public enum BoltzError: Swift.Error { +public enum BroadcastError: Swift.Error { - case InitializationError(errorDetails: String - ) - case ConnectionError(errorDetails: String - ) - case DatabaseError(errorDetails: String - ) - case ApiError(errorDetails: String - ) - case SwapError(errorDetails: String - ) - case BroadcastError(errorDetails: String + case InvalidHex(errorDetails: String ) - case InvalidInput(errorDetails: String + case InvalidTransaction(errorDetails: String ) - case SerializationError(errorDetails: String + case ElectrumError(errorDetails: String ) - case NotFound(errorDetails: String + case TaskError(errorDetails: String ) } @@ -17362,41 +19329,26 @@ public enum BoltzError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { - typealias SwiftType = BoltzError +public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { + typealias SwiftType = BroadcastError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BroadcastError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .DatabaseError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .ApiError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .SwapError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .BroadcastError( + case 1: return .InvalidHex( errorDetails: try FfiConverterString.read(from: &buf) ) - case 7: return .InvalidInput( + case 2: return .InvalidTransaction( errorDetails: try FfiConverterString.read(from: &buf) ) - case 8: return .SerializationError( + case 3: return .ElectrumError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 9: return .NotFound( + case 4: return .TaskError( errorDetails: try FfiConverterString.read(from: &buf) ) @@ -17404,57 +19356,32 @@ public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { } } - public static func write(_ value: BoltzError, into buf: inout [UInt8]) { + public static func write(_ value: BroadcastError, into buf: inout [UInt8]) { switch value { - case let .InitializationError(errorDetails): + case let .InvalidHex(errorDetails): writeInt(&buf, Int32(1)) FfiConverterString.write(errorDetails, into: &buf) - case let .ConnectionError(errorDetails): + case let .InvalidTransaction(errorDetails): writeInt(&buf, Int32(2)) FfiConverterString.write(errorDetails, into: &buf) - case let .DatabaseError(errorDetails): + case let .ElectrumError(errorDetails): writeInt(&buf, Int32(3)) FfiConverterString.write(errorDetails, into: &buf) - case let .ApiError(errorDetails): + case let .TaskError(errorDetails): writeInt(&buf, Int32(4)) FfiConverterString.write(errorDetails, into: &buf) - - case let .SwapError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .BroadcastError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InvalidInput(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .SerializationError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .NotFound(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -17463,26 +19390,26 @@ public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzError_lift(_ buf: RustBuffer) throws -> BoltzError { - return try FfiConverterTypeBoltzError.lift(buf) +public func FfiConverterTypeBroadcastError_lift(_ buf: RustBuffer) throws -> BroadcastError { + return try FfiConverterTypeBroadcastError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzError_lower(_ value: BoltzError) -> RustBuffer { - return FfiConverterTypeBoltzError.lower(value) +public func FfiConverterTypeBroadcastError_lower(_ value: BroadcastError) -> RustBuffer { + return FfiConverterTypeBroadcastError.lower(value) } -extension BoltzError: Equatable, Hashable {} +extension BroadcastError: Equatable, Hashable {} -extension BoltzError: Codable {} +extension BroadcastError: Codable {} -extension BoltzError: Foundation.LocalizedError { +extension BroadcastError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -17493,176 +19420,61 @@ extension BoltzError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Bitcoin network selection for Boltz swaps. Maps to the networks Boltz - * operates on (mainnet, testnet, regtest). - */ - -public enum BoltzNetwork { - - case mainnet - case testnet - case regtest -} - - -#if compiler(>=6) -extension BoltzNetwork: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeBoltzNetwork: FfiConverterRustBuffer { - typealias SwiftType = BoltzNetwork - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzNetwork { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .mainnet - - case 2: return .testnet - - case 3: return .regtest - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: BoltzNetwork, into buf: inout [UInt8]) { - switch value { - - - case .mainnet: - writeInt(&buf, Int32(1)) - - - case .testnet: - writeInt(&buf, Int32(2)) - - - case .regtest: - writeInt(&buf, Int32(3)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeBoltzNetwork_lift(_ buf: RustBuffer) throws -> BoltzNetwork { - return try FfiConverterTypeBoltzNetwork.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeBoltzNetwork_lower(_ value: BoltzNetwork) -> RustBuffer { - return FfiConverterTypeBoltzNetwork.lower(value) -} - - -extension BoltzNetwork: Equatable, Hashable {} - -extension BoltzNetwork: Codable {} - - - - - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Events emitted to a registered [`crate::modules::boltz::BoltzEventListener`] - * as swaps progress through their lifecycle. - */ -public enum BoltzSwapEvent { +public enum BtBolt11InvoiceState { - /** - * The swap transitioned to a new status. - */ - case statusUpdate(swapId: String, status: BoltzSwapStatus - ) - /** - * A reverse swap was claimed onchain. `txid` is the claim transaction. - */ - case claimed(swapId: String, txid: String - ) - /** - * A submarine swap was refunded onchain. `txid` is the refund transaction. - */ - case refunded(swapId: String, txid: String - ) - /** - * An error occurred while processing the swap (e.g. an auto-claim failed). - */ - case error(swapId: String, message: String - ) + case pending + case holding + case paid + case canceled } #if compiler(>=6) -extension BoltzSwapEvent: Sendable {} +extension BtBolt11InvoiceState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapEvent +public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { + typealias SwiftType = BtBolt11InvoiceState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapEvent { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtBolt11InvoiceState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .statusUpdate(swapId: try FfiConverterString.read(from: &buf), status: try FfiConverterTypeBoltzSwapStatus.read(from: &buf) - ) + case 1: return .pending - case 2: return .claimed(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) - ) + case 2: return .holding - case 3: return .refunded(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) - ) + case 3: return .paid - case 4: return .error(swapId: try FfiConverterString.read(from: &buf), message: try FfiConverterString.read(from: &buf) - ) + case 4: return .canceled default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapEvent, into buf: inout [UInt8]) { + public static func write(_ value: BtBolt11InvoiceState, into buf: inout [UInt8]) { switch value { - case let .statusUpdate(swapId,status): + case .pending: writeInt(&buf, Int32(1)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterTypeBoltzSwapStatus.write(status, into: &buf) - - case let .claimed(swapId,txid): + + case .holding: writeInt(&buf, Int32(2)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(txid, into: &buf) - - case let .refunded(swapId,txid): + + case .paid: writeInt(&buf, Int32(3)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(txid, into: &buf) - - case let .error(swapId,message): + + case .canceled: writeInt(&buf, Int32(4)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(message, into: &buf) - + } } } @@ -17671,21 +19483,21 @@ public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapEvent_lift(_ buf: RustBuffer) throws -> BoltzSwapEvent { - return try FfiConverterTypeBoltzSwapEvent.lift(buf) +public func FfiConverterTypeBtBolt11InvoiceState_lift(_ buf: RustBuffer) throws -> BtBolt11InvoiceState { + return try FfiConverterTypeBtBolt11InvoiceState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapEvent_lower(_ value: BoltzSwapEvent) -> RustBuffer { - return FfiConverterTypeBoltzSwapEvent.lower(value) +public func FfiConverterTypeBtBolt11InvoiceState_lower(_ value: BtBolt11InvoiceState) -> RustBuffer { + return FfiConverterTypeBtBolt11InvoiceState.lower(value) } -extension BoltzSwapEvent: Equatable, Hashable {} +extension BtBolt11InvoiceState: Equatable, Hashable {} -extension BoltzSwapEvent: Codable {} +extension BtBolt11InvoiceState: Codable {} @@ -17694,202 +19506,68 @@ extension BoltzSwapEvent: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Typed view of the Boltz swap lifecycle. `Unknown` carries the raw status so - * new server-side states don't break the bindings. - * - * See . - */ -public enum BoltzSwapStatus { +public enum BtChannelOrderErrorType { - /** - * `swap.created` — initial state. - */ - case swapCreated - /** - * `invoice.set` — invoice attached to a submarine swap. - */ - case invoiceSet - /** - * `transaction.mempool` — a lockup transaction is in the mempool. - */ - case transactionMempool - /** - * `transaction.confirmed` — a lockup transaction confirmed. - */ - case transactionConfirmed - /** - * `invoice.pending` — Boltz is paying the submarine swap invoice. - */ - case invoicePending - /** - * `invoice.paid` — submarine swap invoice paid by Boltz. - */ - case invoicePaid - /** - * `invoice.settled` — reverse swap invoice settled (preimage revealed). - */ - case invoiceSettled - /** - * `invoice.failedToPay` — submarine swap invoice could not be paid; refund. - */ - case invoiceFailedToPay - /** - * `invoice.expired` — reverse swap invoice expired before payment. - */ - case invoiceExpired - /** - * `transaction.claim.pending` — Boltz ready for a cooperative claim. - */ - case transactionClaimPending - /** - * `transaction.claimed` — onchain funds claimed. - */ - case transactionClaimed - /** - * `transaction.refunded` — onchain funds refunded. - */ - case transactionRefunded - /** - * `transaction.lockupFailed` — wrong amount locked; can refund. - */ - case transactionLockupFailed - /** - * `transaction.failed` — Boltz failed to lock the agreed funds. - */ - case transactionFailed - /** - * `swap.expired` — swap expired without completing. - */ - case swapExpired - /** - * Any status not yet modelled. `raw` holds the verbatim Boltz status. - */ - case unknown(raw: String - ) + case wrongOrderState + case peerNotReachable + case channelRejectedByDestination + case channelRejectedByLsp + case blocktankNotReady } #if compiler(>=6) -extension BoltzSwapStatus: Sendable {} +extension BtChannelOrderErrorType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapStatus +public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { + typealias SwiftType = BtChannelOrderErrorType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtChannelOrderErrorType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .swapCreated - - case 2: return .invoiceSet - - case 3: return .transactionMempool - - case 4: return .transactionConfirmed - - case 5: return .invoicePending - - case 6: return .invoicePaid - - case 7: return .invoiceSettled - - case 8: return .invoiceFailedToPay - - case 9: return .invoiceExpired - - case 10: return .transactionClaimPending - - case 11: return .transactionClaimed - - case 12: return .transactionRefunded + case 1: return .wrongOrderState - case 13: return .transactionLockupFailed + case 2: return .peerNotReachable - case 14: return .transactionFailed + case 3: return .channelRejectedByDestination - case 15: return .swapExpired + case 4: return .channelRejectedByLsp - case 16: return .unknown(raw: try FfiConverterString.read(from: &buf) - ) + case 5: return .blocktankNotReady default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapStatus, into buf: inout [UInt8]) { + public static func write(_ value: BtChannelOrderErrorType, into buf: inout [UInt8]) { switch value { - case .swapCreated: + case .wrongOrderState: writeInt(&buf, Int32(1)) - case .invoiceSet: + case .peerNotReachable: writeInt(&buf, Int32(2)) - case .transactionMempool: + case .channelRejectedByDestination: writeInt(&buf, Int32(3)) - case .transactionConfirmed: + case .channelRejectedByLsp: writeInt(&buf, Int32(4)) - case .invoicePending: + case .blocktankNotReady: writeInt(&buf, Int32(5)) - - case .invoicePaid: - writeInt(&buf, Int32(6)) - - - case .invoiceSettled: - writeInt(&buf, Int32(7)) - - - case .invoiceFailedToPay: - writeInt(&buf, Int32(8)) - - - case .invoiceExpired: - writeInt(&buf, Int32(9)) - - - case .transactionClaimPending: - writeInt(&buf, Int32(10)) - - - case .transactionClaimed: - writeInt(&buf, Int32(11)) - - - case .transactionRefunded: - writeInt(&buf, Int32(12)) - - - case .transactionLockupFailed: - writeInt(&buf, Int32(13)) - - - case .transactionFailed: - writeInt(&buf, Int32(14)) - - - case .swapExpired: - writeInt(&buf, Int32(15)) - - - case let .unknown(raw): - writeInt(&buf, Int32(16)) - FfiConverterString.write(raw, into: &buf) - } } } @@ -17898,78 +19576,77 @@ public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapStatus_lift(_ buf: RustBuffer) throws -> BoltzSwapStatus { - return try FfiConverterTypeBoltzSwapStatus.lift(buf) +public func FfiConverterTypeBtChannelOrderErrorType_lift(_ buf: RustBuffer) throws -> BtChannelOrderErrorType { + return try FfiConverterTypeBtChannelOrderErrorType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapStatus_lower(_ value: BoltzSwapStatus) -> RustBuffer { - return FfiConverterTypeBoltzSwapStatus.lower(value) +public func FfiConverterTypeBtChannelOrderErrorType_lower(_ value: BtChannelOrderErrorType) -> RustBuffer { + return FfiConverterTypeBtChannelOrderErrorType.lower(value) } -extension BoltzSwapStatus: Equatable, Hashable {} +extension BtChannelOrderErrorType: Equatable, Hashable {} -extension BoltzSwapStatus: Codable {} +extension BtChannelOrderErrorType: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * The direction of a Boltz swap. - * - * - `Submarine`: onchain Bitcoin -> Lightning (the user locks onchain funds, - * Boltz pays a Lightning invoice). - * - `Reverse`: Lightning -> onchain Bitcoin (the user pays a Boltz hold - * invoice, Boltz locks onchain funds the user then claims). - */ +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BoltzSwapType { +public enum BtOpenChannelState { - case submarine - case reverse + case opening + case `open` + case closed } #if compiler(>=6) -extension BoltzSwapType: Sendable {} +extension BtOpenChannelState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapType +public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { + typealias SwiftType = BtOpenChannelState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOpenChannelState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .submarine + case 1: return .opening - case 2: return .reverse + case 2: return .`open` + + case 3: return .closed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapType, into buf: inout [UInt8]) { + public static func write(_ value: BtOpenChannelState, into buf: inout [UInt8]) { switch value { - case .submarine: + case .opening: writeInt(&buf, Int32(1)) - case .reverse: + case .`open`: writeInt(&buf, Int32(2)) + + case .closed: + writeInt(&buf, Int32(3)) + } } } @@ -17978,99 +19655,84 @@ public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapType_lift(_ buf: RustBuffer) throws -> BoltzSwapType { - return try FfiConverterTypeBoltzSwapType.lift(buf) +public func FfiConverterTypeBtOpenChannelState_lift(_ buf: RustBuffer) throws -> BtOpenChannelState { + return try FfiConverterTypeBtOpenChannelState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapType_lower(_ value: BoltzSwapType) -> RustBuffer { - return FfiConverterTypeBoltzSwapType.lower(value) +public func FfiConverterTypeBtOpenChannelState_lower(_ value: BtOpenChannelState) -> RustBuffer { + return FfiConverterTypeBtOpenChannelState.lower(value) } -extension BoltzSwapType: Equatable, Hashable {} - -extension BoltzSwapType: Codable {} +extension BtOpenChannelState: Equatable, Hashable {} +extension BtOpenChannelState: Codable {} -public enum BroadcastError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum BtOrderState { - - case InvalidHex(errorDetails: String - ) - case InvalidTransaction(errorDetails: String - ) - case ElectrumError(errorDetails: String - ) - case TaskError(errorDetails: String - ) + case created + case expired + case `open` + case closed } +#if compiler(>=6) +extension BtOrderState: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { - typealias SwiftType = BroadcastError +public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { + typealias SwiftType = BtOrderState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BroadcastError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .created - case 1: return .InvalidHex( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidTransaction( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .ElectrumError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .TaskError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .expired + + case 3: return .`open` + + case 4: return .closed + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BroadcastError, into buf: inout [UInt8]) { + public static func write(_ value: BtOrderState, into buf: inout [UInt8]) { switch value { - - - - case let .InvalidHex(errorDetails): + case .created: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .InvalidTransaction(errorDetails): + + case .expired: writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .ElectrumError(errorDetails): + + case .`open`: writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .TaskError(errorDetails): + + case .closed: writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - + } } } @@ -18079,30 +19741,23 @@ public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBroadcastError_lift(_ buf: RustBuffer) throws -> BroadcastError { - return try FfiConverterTypeBroadcastError.lift(buf) +public func FfiConverterTypeBtOrderState_lift(_ buf: RustBuffer) throws -> BtOrderState { + return try FfiConverterTypeBtOrderState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBroadcastError_lower(_ value: BroadcastError) -> RustBuffer { - return FfiConverterTypeBroadcastError.lower(value) +public func FfiConverterTypeBtOrderState_lower(_ value: BtOrderState) -> RustBuffer { + return FfiConverterTypeBtOrderState.lower(value) } -extension BroadcastError: Equatable, Hashable {} - -extension BroadcastError: Codable {} - +extension BtOrderState: Equatable, Hashable {} +extension BtOrderState: Codable {} -extension BroadcastError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -18110,58 +19765,58 @@ extension BroadcastError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtBolt11InvoiceState { +public enum BtOrderState2 { - case pending - case holding + case created + case expired + case executed case paid - case canceled } #if compiler(>=6) -extension BtBolt11InvoiceState: Sendable {} +extension BtOrderState2: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { - typealias SwiftType = BtBolt11InvoiceState +public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { + typealias SwiftType = BtOrderState2 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtBolt11InvoiceState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState2 { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .pending + case 1: return .created - case 2: return .holding + case 2: return .expired - case 3: return .paid + case 3: return .executed - case 4: return .canceled + case 4: return .paid default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtBolt11InvoiceState, into buf: inout [UInt8]) { + public static func write(_ value: BtOrderState2, into buf: inout [UInt8]) { switch value { - case .pending: + case .created: writeInt(&buf, Int32(1)) - case .holding: + case .expired: writeInt(&buf, Int32(2)) - case .paid: + case .executed: writeInt(&buf, Int32(3)) - case .canceled: + case .paid: writeInt(&buf, Int32(4)) } @@ -18172,21 +19827,21 @@ public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtBolt11InvoiceState_lift(_ buf: RustBuffer) throws -> BtBolt11InvoiceState { - return try FfiConverterTypeBtBolt11InvoiceState.lift(buf) +public func FfiConverterTypeBtOrderState2_lift(_ buf: RustBuffer) throws -> BtOrderState2 { + return try FfiConverterTypeBtOrderState2.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtBolt11InvoiceState_lower(_ value: BtBolt11InvoiceState) -> RustBuffer { - return FfiConverterTypeBtBolt11InvoiceState.lower(value) +public func FfiConverterTypeBtOrderState2_lower(_ value: BtOrderState2) -> RustBuffer { + return FfiConverterTypeBtOrderState2.lower(value) } -extension BtBolt11InvoiceState: Equatable, Hashable {} +extension BtOrderState2: Equatable, Hashable {} -extension BtBolt11InvoiceState: Codable {} +extension BtOrderState2: Codable {} @@ -18196,65 +19851,65 @@ extension BtBolt11InvoiceState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtChannelOrderErrorType { +public enum BtPaymentState { - case wrongOrderState - case peerNotReachable - case channelRejectedByDestination - case channelRejectedByLsp - case blocktankNotReady + case created + case partiallyPaid + case paid + case refunded + case refundAvailable } #if compiler(>=6) -extension BtChannelOrderErrorType: Sendable {} +extension BtPaymentState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { - typealias SwiftType = BtChannelOrderErrorType +public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { + typealias SwiftType = BtPaymentState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtChannelOrderErrorType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .wrongOrderState + case 1: return .created - case 2: return .peerNotReachable + case 2: return .partiallyPaid - case 3: return .channelRejectedByDestination + case 3: return .paid - case 4: return .channelRejectedByLsp + case 4: return .refunded - case 5: return .blocktankNotReady + case 5: return .refundAvailable default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtChannelOrderErrorType, into buf: inout [UInt8]) { + public static func write(_ value: BtPaymentState, into buf: inout [UInt8]) { switch value { - case .wrongOrderState: + case .created: writeInt(&buf, Int32(1)) - case .peerNotReachable: + case .partiallyPaid: writeInt(&buf, Int32(2)) - case .channelRejectedByDestination: + case .paid: writeInt(&buf, Int32(3)) - case .channelRejectedByLsp: + case .refunded: writeInt(&buf, Int32(4)) - case .blocktankNotReady: + case .refundAvailable: writeInt(&buf, Int32(5)) } @@ -18265,21 +19920,21 @@ public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtChannelOrderErrorType_lift(_ buf: RustBuffer) throws -> BtChannelOrderErrorType { - return try FfiConverterTypeBtChannelOrderErrorType.lift(buf) +public func FfiConverterTypeBtPaymentState_lift(_ buf: RustBuffer) throws -> BtPaymentState { + return try FfiConverterTypeBtPaymentState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtChannelOrderErrorType_lower(_ value: BtChannelOrderErrorType) -> RustBuffer { - return FfiConverterTypeBtChannelOrderErrorType.lower(value) +public func FfiConverterTypeBtPaymentState_lower(_ value: BtPaymentState) -> RustBuffer { + return FfiConverterTypeBtPaymentState.lower(value) } -extension BtChannelOrderErrorType: Equatable, Hashable {} +extension BtPaymentState: Equatable, Hashable {} -extension BtChannelOrderErrorType: Codable {} +extension BtPaymentState: Codable {} @@ -18289,53 +19944,67 @@ extension BtChannelOrderErrorType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtOpenChannelState { +public enum BtPaymentState2 { - case opening - case `open` - case closed + case created + case paid + case refunded + case refundAvailable + case canceled } #if compiler(>=6) -extension BtOpenChannelState: Sendable {} +extension BtPaymentState2: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) -#endif -public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { - typealias SwiftType = BtOpenChannelState +#endif +public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { + typealias SwiftType = BtPaymentState2 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOpenChannelState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState2 { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .opening + case 1: return .created - case 2: return .`open` + case 2: return .paid - case 3: return .closed + case 3: return .refunded + + case 4: return .refundAvailable + + case 5: return .canceled default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOpenChannelState, into buf: inout [UInt8]) { + public static func write(_ value: BtPaymentState2, into buf: inout [UInt8]) { switch value { - case .opening: + case .created: writeInt(&buf, Int32(1)) - case .`open`: + case .paid: writeInt(&buf, Int32(2)) - case .closed: + case .refunded: writeInt(&buf, Int32(3)) + + case .refundAvailable: + writeInt(&buf, Int32(4)) + + + case .canceled: + writeInt(&buf, Int32(5)) + } } } @@ -18344,21 +20013,21 @@ public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOpenChannelState_lift(_ buf: RustBuffer) throws -> BtOpenChannelState { - return try FfiConverterTypeBtOpenChannelState.lift(buf) +public func FfiConverterTypeBtPaymentState2_lift(_ buf: RustBuffer) throws -> BtPaymentState2 { + return try FfiConverterTypeBtPaymentState2.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOpenChannelState_lower(_ value: BtOpenChannelState) -> RustBuffer { - return FfiConverterTypeBtOpenChannelState.lower(value) +public func FfiConverterTypeBtPaymentState2_lower(_ value: BtPaymentState2) -> RustBuffer { + return FfiConverterTypeBtPaymentState2.lower(value) } -extension BtOpenChannelState: Equatable, Hashable {} +extension BtPaymentState2: Equatable, Hashable {} -extension BtOpenChannelState: Codable {} +extension BtPaymentState2: Codable {} @@ -18368,42 +20037,42 @@ extension BtOpenChannelState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtOrderState { +public enum CJitStateEnum { case created + case completed case expired - case `open` - case closed + case failed } #if compiler(>=6) -extension BtOrderState: Sendable {} +extension CJitStateEnum: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { - typealias SwiftType = BtOrderState +public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { + typealias SwiftType = CJitStateEnum - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CJitStateEnum { let variant: Int32 = try readInt(&buf) switch variant { case 1: return .created - case 2: return .expired + case 2: return .completed - case 3: return .`open` + case 3: return .expired - case 4: return .closed + case 4: return .failed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOrderState, into buf: inout [UInt8]) { + public static func write(_ value: CJitStateEnum, into buf: inout [UInt8]) { switch value { @@ -18411,15 +20080,15 @@ public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { writeInt(&buf, Int32(1)) - case .expired: + case .completed: writeInt(&buf, Int32(2)) - case .`open`: + case .expired: writeInt(&buf, Int32(3)) - case .closed: + case .failed: writeInt(&buf, Int32(4)) } @@ -18430,21 +20099,21 @@ public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState_lift(_ buf: RustBuffer) throws -> BtOrderState { - return try FfiConverterTypeBtOrderState.lift(buf) +public func FfiConverterTypeCJitStateEnum_lift(_ buf: RustBuffer) throws -> CJitStateEnum { + return try FfiConverterTypeCJitStateEnum.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState_lower(_ value: BtOrderState) -> RustBuffer { - return FfiConverterTypeBtOrderState.lower(value) +public func FfiConverterTypeCJitStateEnum_lower(_ value: CJitStateEnum) -> RustBuffer { + return FfiConverterTypeCJitStateEnum.lower(value) } -extension BtOrderState: Equatable, Hashable {} +extension CJitStateEnum: Equatable, Hashable {} -extension BtOrderState: Codable {} +extension CJitStateEnum: Codable {} @@ -18453,61 +20122,66 @@ extension BtOrderState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Coin selection strategy for transaction composition. + */ -public enum BtOrderState2 { +public enum CoinSelection { - case created - case expired - case executed - case paid + /** + * Branch-and-bound (default). Minimizes change by searching for exact matches. + */ + case branchAndBound + /** + * Selects largest UTXOs first. Useful for UTXO consolidation. + */ + case largestFirst + /** + * Selects oldest UTXOs first. Maximizes coin-age spending. + */ + case oldestFirst } #if compiler(>=6) -extension BtOrderState2: Sendable {} +extension CoinSelection: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { - typealias SwiftType = BtOrderState2 +public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { + typealias SwiftType = CoinSelection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState2 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CoinSelection { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .expired + case 1: return .branchAndBound - case 3: return .executed + case 2: return .largestFirst - case 4: return .paid + case 3: return .oldestFirst default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOrderState2, into buf: inout [UInt8]) { + public static func write(_ value: CoinSelection, into buf: inout [UInt8]) { switch value { - case .created: + case .branchAndBound: writeInt(&buf, Int32(1)) - case .expired: + case .largestFirst: writeInt(&buf, Int32(2)) - case .executed: + case .oldestFirst: writeInt(&buf, Int32(3)) - - case .paid: - writeInt(&buf, Int32(4)) - } } } @@ -18516,21 +20190,21 @@ public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState2_lift(_ buf: RustBuffer) throws -> BtOrderState2 { - return try FfiConverterTypeBtOrderState2.lift(buf) +public func FfiConverterTypeCoinSelection_lift(_ buf: RustBuffer) throws -> CoinSelection { + return try FfiConverterTypeCoinSelection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState2_lower(_ value: BtOrderState2) -> RustBuffer { - return FfiConverterTypeBtOrderState2.lower(value) +public func FfiConverterTypeCoinSelection_lower(_ value: CoinSelection) -> RustBuffer { + return FfiConverterTypeCoinSelection.lower(value) } -extension BtOrderState2: Equatable, Hashable {} +extension CoinSelection: Equatable, Hashable {} -extension BtOrderState2: Codable {} +extension CoinSelection: Codable {} @@ -18539,68 +20213,76 @@ extension BtOrderState2: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Output specification for transaction composition. + */ -public enum BtPaymentState { +public enum ComposeOutput { - case created - case partiallyPaid - case paid - case refunded - case refundAvailable + /** + * Payment to a specific address with a fixed amount (satoshis) + */ + case payment(address: String, amountSats: UInt64 + ) + /** + * Send all remaining funds (after fees) to an address + */ + case sendMax(address: String + ) + /** + * OP_RETURN data output (hex-encoded payload) + */ + case opReturn(dataHex: String + ) } #if compiler(>=6) -extension BtPaymentState: Sendable {} +extension ComposeOutput: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { - typealias SwiftType = BtPaymentState +public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { + typealias SwiftType = ComposeOutput - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeOutput { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .partiallyPaid - - case 3: return .paid + case 1: return .payment(address: try FfiConverterString.read(from: &buf), amountSats: try FfiConverterUInt64.read(from: &buf) + ) - case 4: return .refunded + case 2: return .sendMax(address: try FfiConverterString.read(from: &buf) + ) - case 5: return .refundAvailable + case 3: return .opReturn(dataHex: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtPaymentState, into buf: inout [UInt8]) { + public static func write(_ value: ComposeOutput, into buf: inout [UInt8]) { switch value { - case .created: + case let .payment(address,amountSats): writeInt(&buf, Int32(1)) + FfiConverterString.write(address, into: &buf) + FfiConverterUInt64.write(amountSats, into: &buf) + - - case .partiallyPaid: + case let .sendMax(address): writeInt(&buf, Int32(2)) + FfiConverterString.write(address, into: &buf) + - - case .paid: + case let .opReturn(dataHex): writeInt(&buf, Int32(3)) - - - case .refunded: - writeInt(&buf, Int32(4)) - - - case .refundAvailable: - writeInt(&buf, Int32(5)) - + FfiConverterString.write(dataHex, into: &buf) + } } } @@ -18609,21 +20291,21 @@ public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState_lift(_ buf: RustBuffer) throws -> BtPaymentState { - return try FfiConverterTypeBtPaymentState.lift(buf) +public func FfiConverterTypeComposeOutput_lift(_ buf: RustBuffer) throws -> ComposeOutput { + return try FfiConverterTypeComposeOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState_lower(_ value: BtPaymentState) -> RustBuffer { - return FfiConverterTypeBtPaymentState.lower(value) +public func FfiConverterTypeComposeOutput_lower(_ value: ComposeOutput) -> RustBuffer { + return FfiConverterTypeComposeOutput.lower(value) } -extension BtPaymentState: Equatable, Hashable {} +extension ComposeOutput: Equatable, Hashable {} -extension BtPaymentState: Codable {} +extension ComposeOutput: Codable {} @@ -18632,68 +20314,79 @@ extension BtPaymentState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Result of composing a transaction at a single fee rate. + */ -public enum BtPaymentState2 { +public enum ComposeResult { - case created - case paid - case refunded - case refundAvailable - case canceled + /** + * Successfully built a signable PSBT + */ + case success( + /** + * Base64-encoded PSBT ready for signing + */psbt: String, + /** + * Total fee in satoshis + */fee: UInt64, + /** + * Target fee rate in sat/vB (actual may differ slightly due to rounding) + */feeRate: Float, + /** + * Total value spent (payments + fee, excluding change). + * Uses BDK's `sent - received` semantics, which may undercount for + * self-transfers where the destination is also owned by the wallet. + */totalSpent: UInt64 + ) + /** + * Composition failed (e.g. insufficient funds) + */ + case error(error: String + ) } #if compiler(>=6) -extension BtPaymentState2: Sendable {} +extension ComposeResult: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { - typealias SwiftType = BtPaymentState2 +public struct FfiConverterTypeComposeResult: FfiConverterRustBuffer { + typealias SwiftType = ComposeResult - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState2 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeResult { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .paid - - case 3: return .refunded - - case 4: return .refundAvailable + case 1: return .success(psbt: try FfiConverterString.read(from: &buf), fee: try FfiConverterUInt64.read(from: &buf), feeRate: try FfiConverterFloat.read(from: &buf), totalSpent: try FfiConverterUInt64.read(from: &buf) + ) - case 5: return .canceled + case 2: return .error(error: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtPaymentState2, into buf: inout [UInt8]) { + public static func write(_ value: ComposeResult, into buf: inout [UInt8]) { switch value { - case .created: + case let .success(psbt,fee,feeRate,totalSpent): writeInt(&buf, Int32(1)) + FfiConverterString.write(psbt, into: &buf) + FfiConverterUInt64.write(fee, into: &buf) + FfiConverterFloat.write(feeRate, into: &buf) + FfiConverterUInt64.write(totalSpent, into: &buf) + - - case .paid: + case let .error(error): writeInt(&buf, Int32(2)) - - - case .refunded: - writeInt(&buf, Int32(3)) - - - case .refundAvailable: - writeInt(&buf, Int32(4)) - - - case .canceled: - writeInt(&buf, Int32(5)) - + FfiConverterString.write(error, into: &buf) + } } } @@ -18702,84 +20395,99 @@ public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState2_lift(_ buf: RustBuffer) throws -> BtPaymentState2 { - return try FfiConverterTypeBtPaymentState2.lift(buf) +public func FfiConverterTypeComposeResult_lift(_ buf: RustBuffer) throws -> ComposeResult { + return try FfiConverterTypeComposeResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState2_lower(_ value: BtPaymentState2) -> RustBuffer { - return FfiConverterTypeBtPaymentState2.lower(value) +public func FfiConverterTypeComposeResult_lower(_ value: ComposeResult) -> RustBuffer { + return FfiConverterTypeComposeResult.lower(value) } -extension BtPaymentState2: Equatable, Hashable {} +extension ComposeResult: Equatable, Hashable {} -extension BtPaymentState2: Codable {} +extension ComposeResult: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum CJitStateEnum { +public enum DbError: Swift.Error { + - case created - case completed - case expired - case failed + + case DbActivityError(errorDetails: ActivityError + ) + case DbBlocktankError(errorDetails: BlocktankError + ) + case DbBoltzError(errorDetails: BoltzError + ) + case InitializationError(errorDetails: String + ) } -#if compiler(>=6) -extension CJitStateEnum: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { - typealias SwiftType = CJitStateEnum +public struct FfiConverterTypeDbError: FfiConverterRustBuffer { + typealias SwiftType = DbError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CJitStateEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DbError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .created - - case 2: return .completed - - case 3: return .expired - - case 4: return .failed + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .DbActivityError( + errorDetails: try FfiConverterTypeActivityError.read(from: &buf) + ) + case 2: return .DbBlocktankError( + errorDetails: try FfiConverterTypeBlocktankError.read(from: &buf) + ) + case 3: return .DbBoltzError( + errorDetails: try FfiConverterTypeBoltzError.read(from: &buf) + ) + case 4: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: CJitStateEnum, into buf: inout [UInt8]) { + public static func write(_ value: DbError, into buf: inout [UInt8]) { switch value { + + - case .created: - writeInt(&buf, Int32(1)) + case let .DbActivityError(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterTypeActivityError.write(errorDetails, into: &buf) + - case .completed: + case let .DbBlocktankError(errorDetails): writeInt(&buf, Int32(2)) + FfiConverterTypeBlocktankError.write(errorDetails, into: &buf) + - - case .expired: + case let .DbBoltzError(errorDetails): writeInt(&buf, Int32(3)) + FfiConverterTypeBoltzError.write(errorDetails, into: &buf) + - - case .failed: + case let .InitializationError(errorDetails): writeInt(&buf, Int32(4)) - + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -18788,89 +20496,150 @@ public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCJitStateEnum_lift(_ buf: RustBuffer) throws -> CJitStateEnum { - return try FfiConverterTypeCJitStateEnum.lift(buf) +public func FfiConverterTypeDbError_lift(_ buf: RustBuffer) throws -> DbError { + return try FfiConverterTypeDbError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCJitStateEnum_lower(_ value: CJitStateEnum) -> RustBuffer { - return FfiConverterTypeCJitStateEnum.lower(value) +public func FfiConverterTypeDbError_lower(_ value: DbError) -> RustBuffer { + return FfiConverterTypeDbError.lower(value) } -extension CJitStateEnum: Equatable, Hashable {} +extension DbError: Equatable, Hashable {} -extension CJitStateEnum: Codable {} +extension DbError: Codable {} +extension DbError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Coin selection strategy for transaction composition. - */ -public enum CoinSelection { + + +public enum DecodingError: Swift.Error { + - /** - * Branch-and-bound (default). Minimizes change by searching for exact matches. - */ - case branchAndBound - /** - * Selects largest UTXOs first. Useful for UTXO consolidation. - */ - case largestFirst - /** - * Selects oldest UTXOs first. Maximizes coin-age spending. - */ - case oldestFirst + + case InvalidFormat + case InvalidNetwork + case InvalidAmount + case InvalidLnurlPayAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 + ) + case InvalidTimestamp + case InvalidChecksum + case InvalidResponse + case UnsupportedType + case InvalidAddress + case RequestFailed + case ClientCreationFailed + case InvoiceCreationFailed(errorMessage: String + ) } -#if compiler(>=6) -extension CoinSelection: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { - typealias SwiftType = CoinSelection +public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { + typealias SwiftType = DecodingError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CoinSelection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodingError { let variant: Int32 = try readInt(&buf) switch variant { + + + + + case 1: return .InvalidFormat + case 2: return .InvalidNetwork + case 3: return .InvalidAmount + case 4: return .InvalidLnurlPayAmount( + amountSatoshis: try FfiConverterUInt64.read(from: &buf), + min: try FfiConverterUInt64.read(from: &buf), + max: try FfiConverterUInt64.read(from: &buf) + ) + case 5: return .InvalidTimestamp + case 6: return .InvalidChecksum + case 7: return .InvalidResponse + case 8: return .UnsupportedType + case 9: return .InvalidAddress + case 10: return .RequestFailed + case 11: return .ClientCreationFailed + case 12: return .InvoiceCreationFailed( + errorMessage: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: DecodingError, into buf: inout [UInt8]) { + switch value { + + + + + + case .InvalidFormat: + writeInt(&buf, Int32(1)) + + + case .InvalidNetwork: + writeInt(&buf, Int32(2)) + + + case .InvalidAmount: + writeInt(&buf, Int32(3)) + + + case let .InvalidLnurlPayAmount(amountSatoshis,min,max): + writeInt(&buf, Int32(4)) + FfiConverterUInt64.write(amountSatoshis, into: &buf) + FfiConverterUInt64.write(min, into: &buf) + FfiConverterUInt64.write(max, into: &buf) + - case 1: return .branchAndBound + case .InvalidTimestamp: + writeInt(&buf, Int32(5)) - case 2: return .largestFirst - case 3: return .oldestFirst + case .InvalidChecksum: + writeInt(&buf, Int32(6)) - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: CoinSelection, into buf: inout [UInt8]) { - switch value { + + case .InvalidResponse: + writeInt(&buf, Int32(7)) - case .branchAndBound: - writeInt(&buf, Int32(1)) + case .UnsupportedType: + writeInt(&buf, Int32(8)) - case .largestFirst: - writeInt(&buf, Int32(2)) + case .InvalidAddress: + writeInt(&buf, Int32(9)) - case .oldestFirst: - writeInt(&buf, Int32(3)) + case .RequestFailed: + writeInt(&buf, Int32(10)) + + + case .ClientCreationFailed: + writeInt(&buf, Int32(11)) + + case let .InvoiceCreationFailed(errorMessage): + writeInt(&buf, Int32(12)) + FfiConverterString.write(errorMessage, into: &buf) + } } } @@ -18879,99 +20648,87 @@ public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCoinSelection_lift(_ buf: RustBuffer) throws -> CoinSelection { - return try FfiConverterTypeCoinSelection.lift(buf) +public func FfiConverterTypeDecodingError_lift(_ buf: RustBuffer) throws -> DecodingError { + return try FfiConverterTypeDecodingError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCoinSelection_lower(_ value: CoinSelection) -> RustBuffer { - return FfiConverterTypeCoinSelection.lower(value) +public func FfiConverterTypeDecodingError_lower(_ value: DecodingError) -> RustBuffer { + return FfiConverterTypeDecodingError.lower(value) } -extension CoinSelection: Equatable, Hashable {} +extension DecodingError: Equatable, Hashable {} + +extension DecodingError: Codable {} -extension CoinSelection: Codable {} +extension DecodingError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Output specification for transaction composition. + * How an application exchanges data with a hardware wallet. */ -public enum ComposeOutput { +public enum HardwareWalletTransport { - /** - * Payment to a specific address with a fixed amount (satoshis) - */ - case payment(address: String, amountSats: UInt64 - ) - /** - * Send all remaining funds (after fees) to an address - */ - case sendMax(address: String - ) - /** - * OP_RETURN data output (hex-encoded payload) - */ - case opReturn(dataHex: String - ) + case usb + case bluetooth + case qr } #if compiler(>=6) -extension ComposeOutput: Sendable {} +extension HardwareWalletTransport: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { - typealias SwiftType = ComposeOutput +public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { + typealias SwiftType = HardwareWalletTransport - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeOutput { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletTransport { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .payment(address: try FfiConverterString.read(from: &buf), amountSats: try FfiConverterUInt64.read(from: &buf) - ) + case 1: return .usb - case 2: return .sendMax(address: try FfiConverterString.read(from: &buf) - ) + case 2: return .bluetooth - case 3: return .opReturn(dataHex: try FfiConverterString.read(from: &buf) - ) + case 3: return .qr default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ComposeOutput, into buf: inout [UInt8]) { + public static func write(_ value: HardwareWalletTransport, into buf: inout [UInt8]) { switch value { - case let .payment(address,amountSats): + case .usb: writeInt(&buf, Int32(1)) - FfiConverterString.write(address, into: &buf) - FfiConverterUInt64.write(amountSats, into: &buf) - - case let .sendMax(address): + + case .bluetooth: writeInt(&buf, Int32(2)) - FfiConverterString.write(address, into: &buf) - - case let .opReturn(dataHex): + + case .qr: writeInt(&buf, Int32(3)) - FfiConverterString.write(dataHex, into: &buf) - + } } } @@ -18980,21 +20737,21 @@ public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeOutput_lift(_ buf: RustBuffer) throws -> ComposeOutput { - return try FfiConverterTypeComposeOutput.lift(buf) +public func FfiConverterTypeHardwareWalletTransport_lift(_ buf: RustBuffer) throws -> HardwareWalletTransport { + return try FfiConverterTypeHardwareWalletTransport.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeOutput_lower(_ value: ComposeOutput) -> RustBuffer { - return FfiConverterTypeComposeOutput.lower(value) +public func FfiConverterTypeHardwareWalletTransport_lower(_ value: HardwareWalletTransport) -> RustBuffer { + return FfiConverterTypeHardwareWalletTransport.lower(value) } -extension ComposeOutput: Equatable, Hashable {} +extension HardwareWalletTransport: Equatable, Hashable {} -extension ComposeOutput: Codable {} +extension HardwareWalletTransport: Codable {} @@ -19004,78 +20761,56 @@ extension ComposeOutput: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Result of composing a transaction at a single fee rate. + * A hardware-wallet vendor recognized by Bitkit. */ -public enum ComposeResult { +public enum HardwareWalletVendor { - /** - * Successfully built a signable PSBT - */ - case success( - /** - * Base64-encoded PSBT ready for signing - */psbt: String, - /** - * Total fee in satoshis - */fee: UInt64, - /** - * Target fee rate in sat/vB (actual may differ slightly due to rounding) - */feeRate: Float, - /** - * Total value spent (payments + fee, excluding change). - * Uses BDK's `sent - received` semantics, which may undercount for - * self-transfers where the destination is also owned by the wallet. - */totalSpent: UInt64 - ) - /** - * Composition failed (e.g. insufficient funds) - */ - case error(error: String - ) + case trezor + case foundation + case blockstream } #if compiler(>=6) -extension ComposeResult: Sendable {} +extension HardwareWalletVendor: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeResult: FfiConverterRustBuffer { - typealias SwiftType = ComposeResult +public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { + typealias SwiftType = HardwareWalletVendor - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeResult { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletVendor { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .success(psbt: try FfiConverterString.read(from: &buf), fee: try FfiConverterUInt64.read(from: &buf), feeRate: try FfiConverterFloat.read(from: &buf), totalSpent: try FfiConverterUInt64.read(from: &buf) - ) + case 1: return .trezor - case 2: return .error(error: try FfiConverterString.read(from: &buf) - ) + case 2: return .foundation + + case 3: return .blockstream default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ComposeResult, into buf: inout [UInt8]) { + public static func write(_ value: HardwareWalletVendor, into buf: inout [UInt8]) { switch value { - case let .success(psbt,fee,feeRate,totalSpent): + case .trezor: writeInt(&buf, Int32(1)) - FfiConverterString.write(psbt, into: &buf) - FfiConverterUInt64.write(fee, into: &buf) - FfiConverterFloat.write(feeRate, into: &buf) - FfiConverterUInt64.write(totalSpent, into: &buf) - - case let .error(error): + + case .foundation: writeInt(&buf, Int32(2)) - FfiConverterString.write(error, into: &buf) - + + + case .blockstream: + writeInt(&buf, Int32(3)) + } } } @@ -19084,99 +20819,84 @@ public struct FfiConverterTypeComposeResult: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeResult_lift(_ buf: RustBuffer) throws -> ComposeResult { - return try FfiConverterTypeComposeResult.lift(buf) +public func FfiConverterTypeHardwareWalletVendor_lift(_ buf: RustBuffer) throws -> HardwareWalletVendor { + return try FfiConverterTypeHardwareWalletVendor.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeResult_lower(_ value: ComposeResult) -> RustBuffer { - return FfiConverterTypeComposeResult.lower(value) +public func FfiConverterTypeHardwareWalletVendor_lower(_ value: HardwareWalletVendor) -> RustBuffer { + return FfiConverterTypeHardwareWalletVendor.lower(value) } -extension ComposeResult: Equatable, Hashable {} - -extension ComposeResult: Codable {} +extension HardwareWalletVendor: Equatable, Hashable {} +extension HardwareWalletVendor: Codable {} -public enum DbError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum JadeAddressVariant { - - case DbActivityError(errorDetails: ActivityError - ) - case DbBlocktankError(errorDetails: BlocktankError - ) - case DbBoltzError(errorDetails: BoltzError - ) - case InitializationError(errorDetails: String - ) + case pkh + case wpkh + case shWpkh + case tr } +#if compiler(>=6) +extension JadeAddressVariant: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeDbError: FfiConverterRustBuffer { - typealias SwiftType = DbError +public struct FfiConverterTypeJadeAddressVariant: FfiConverterRustBuffer { + typealias SwiftType = JadeAddressVariant - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DbError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAddressVariant { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .pkh - case 1: return .DbActivityError( - errorDetails: try FfiConverterTypeActivityError.read(from: &buf) - ) - case 2: return .DbBlocktankError( - errorDetails: try FfiConverterTypeBlocktankError.read(from: &buf) - ) - case 3: return .DbBoltzError( - errorDetails: try FfiConverterTypeBoltzError.read(from: &buf) - ) - case 4: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .wpkh + + case 3: return .shWpkh + + case 4: return .tr + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: DbError, into buf: inout [UInt8]) { + public static func write(_ value: JadeAddressVariant, into buf: inout [UInt8]) { switch value { - - - - case let .DbActivityError(errorDetails): + case .pkh: writeInt(&buf, Int32(1)) - FfiConverterTypeActivityError.write(errorDetails, into: &buf) - - case let .DbBlocktankError(errorDetails): + + case .wpkh: writeInt(&buf, Int32(2)) - FfiConverterTypeBlocktankError.write(errorDetails, into: &buf) - - case let .DbBoltzError(errorDetails): + + case .shWpkh: writeInt(&buf, Int32(3)) - FfiConverterTypeBoltzError.write(errorDetails, into: &buf) - - case let .InitializationError(errorDetails): + + case .tr: writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - + } } } @@ -19185,52 +20905,68 @@ public struct FfiConverterTypeDbError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDbError_lift(_ buf: RustBuffer) throws -> DbError { - return try FfiConverterTypeDbError.lift(buf) +public func FfiConverterTypeJadeAddressVariant_lift(_ buf: RustBuffer) throws -> JadeAddressVariant { + return try FfiConverterTypeJadeAddressVariant.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDbError_lower(_ value: DbError) -> RustBuffer { - return FfiConverterTypeDbError.lower(value) +public func FfiConverterTypeJadeAddressVariant_lower(_ value: JadeAddressVariant) -> RustBuffer { + return FfiConverterTypeJadeAddressVariant.lower(value) } -extension DbError: Equatable, Hashable {} - -extension DbError: Codable {} - +extension JadeAddressVariant: Equatable, Hashable {} +extension JadeAddressVariant: Codable {} -extension DbError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -public enum DecodingError: Swift.Error { +public enum JadeError: Swift.Error { - case InvalidFormat - case InvalidNetwork - case InvalidAmount - case InvalidLnurlPayAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 + case TransportError(errorDetails: String ) - case InvalidTimestamp - case InvalidChecksum - case InvalidResponse - case UnsupportedType - case InvalidAddress - case RequestFailed - case ClientCreationFailed - case InvoiceCreationFailed(errorMessage: String + case DeviceNotFound + case DeviceDisconnected + case DeviceBusy + case NotConnected + case NotInitialized + case ConnectionError(errorDetails: String + ) + case ProtocolError(errorDetails: String + ) + case Timeout + case UserCancelled + case DeviceLocked + case DeviceUninitialized + case InvalidPin + case NetworkMismatch(errorDetails: String + ) + case UnsupportedFirmware(installed: String, required: String + ) + case InvalidPath(errorDetails: String + ) + case InvalidPsbt(errorDetails: String + ) + case PsbtTooLarge(size: UInt64, max: UInt64 + ) + case FingerprintMismatch(device: String, psbt: String + ) + case NothingSigned + case AddressMismatch(expected: String, returned: String + ) + case PinServerError(errorDetails: String + ) + case DeviceError(errorDetails: String + ) + case IoError(errorDetails: String ) } @@ -19238,96 +20974,193 @@ public enum DecodingError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { - typealias SwiftType = DecodingError +public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { + typealias SwiftType = JadeError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodingError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidFormat - case 2: return .InvalidNetwork - case 3: return .InvalidAmount - case 4: return .InvalidLnurlPayAmount( - amountSatoshis: try FfiConverterUInt64.read(from: &buf), - min: try FfiConverterUInt64.read(from: &buf), + case 1: return .TransportError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .DeviceNotFound + case 3: return .DeviceDisconnected + case 4: return .DeviceBusy + case 5: return .NotConnected + case 6: return .NotInitialized + case 7: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .ProtocolError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .Timeout + case 10: return .UserCancelled + case 11: return .DeviceLocked + case 12: return .DeviceUninitialized + case 13: return .InvalidPin + case 14: return .NetworkMismatch( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 15: return .UnsupportedFirmware( + installed: try FfiConverterString.read(from: &buf), + required: try FfiConverterString.read(from: &buf) + ) + case 16: return .InvalidPath( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 17: return .InvalidPsbt( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 18: return .PsbtTooLarge( + size: try FfiConverterUInt64.read(from: &buf), max: try FfiConverterUInt64.read(from: &buf) ) - case 5: return .InvalidTimestamp - case 6: return .InvalidChecksum - case 7: return .InvalidResponse - case 8: return .UnsupportedType - case 9: return .InvalidAddress - case 10: return .RequestFailed - case 11: return .ClientCreationFailed - case 12: return .InvoiceCreationFailed( - errorMessage: try FfiConverterString.read(from: &buf) + case 19: return .FingerprintMismatch( + device: try FfiConverterString.read(from: &buf), + psbt: try FfiConverterString.read(from: &buf) + ) + case 20: return .NothingSigned + case 21: return .AddressMismatch( + expected: try FfiConverterString.read(from: &buf), + returned: try FfiConverterString.read(from: &buf) + ) + case 22: return .PinServerError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 23: return .DeviceError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 24: return .IoError( + errorDetails: try FfiConverterString.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: DecodingError, into buf: inout [UInt8]) { + public static func write(_ value: JadeError, into buf: inout [UInt8]) { switch value { - case .InvalidFormat: + case let .TransportError(errorDetails): writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + - - case .InvalidNetwork: + case .DeviceNotFound: writeInt(&buf, Int32(2)) - case .InvalidAmount: + case .DeviceDisconnected: writeInt(&buf, Int32(3)) - case let .InvalidLnurlPayAmount(amountSatoshis,min,max): + case .DeviceBusy: writeInt(&buf, Int32(4)) - FfiConverterUInt64.write(amountSatoshis, into: &buf) - FfiConverterUInt64.write(min, into: &buf) - FfiConverterUInt64.write(max, into: &buf) - - case .InvalidTimestamp: + + case .NotConnected: writeInt(&buf, Int32(5)) - case .InvalidChecksum: + case .NotInitialized: writeInt(&buf, Int32(6)) - case .InvalidResponse: + case let .ConnectionError(errorDetails): writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + - - case .UnsupportedType: + case let .ProtocolError(errorDetails): writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + - - case .InvalidAddress: + case .Timeout: writeInt(&buf, Int32(9)) - case .RequestFailed: + case .UserCancelled: writeInt(&buf, Int32(10)) - case .ClientCreationFailed: - writeInt(&buf, Int32(11)) + case .DeviceLocked: + writeInt(&buf, Int32(11)) + + + case .DeviceUninitialized: + writeInt(&buf, Int32(12)) + + + case .InvalidPin: + writeInt(&buf, Int32(13)) + + + case let .NetworkMismatch(errorDetails): + writeInt(&buf, Int32(14)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .UnsupportedFirmware(installed,required): + writeInt(&buf, Int32(15)) + FfiConverterString.write(installed, into: &buf) + FfiConverterString.write(required, into: &buf) + + + case let .InvalidPath(errorDetails): + writeInt(&buf, Int32(16)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidPsbt(errorDetails): + writeInt(&buf, Int32(17)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .PsbtTooLarge(size,max): + writeInt(&buf, Int32(18)) + FfiConverterUInt64.write(size, into: &buf) + FfiConverterUInt64.write(max, into: &buf) + + + case let .FingerprintMismatch(device,psbt): + writeInt(&buf, Int32(19)) + FfiConverterString.write(device, into: &buf) + FfiConverterString.write(psbt, into: &buf) + + + case .NothingSigned: + writeInt(&buf, Int32(20)) + + + case let .AddressMismatch(expected,returned): + writeInt(&buf, Int32(21)) + FfiConverterString.write(expected, into: &buf) + FfiConverterString.write(returned, into: &buf) + + + case let .PinServerError(errorDetails): + writeInt(&buf, Int32(22)) + FfiConverterString.write(errorDetails, into: &buf) + + case let .DeviceError(errorDetails): + writeInt(&buf, Int32(23)) + FfiConverterString.write(errorDetails, into: &buf) + - case let .InvoiceCreationFailed(errorMessage): - writeInt(&buf, Int32(12)) - FfiConverterString.write(errorMessage, into: &buf) + case let .IoError(errorDetails): + writeInt(&buf, Int32(24)) + FfiConverterString.write(errorDetails, into: &buf) } } @@ -19337,26 +21170,26 @@ public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDecodingError_lift(_ buf: RustBuffer) throws -> DecodingError { - return try FfiConverterTypeDecodingError.lift(buf) +public func FfiConverterTypeJadeError_lift(_ buf: RustBuffer) throws -> JadeError { + return try FfiConverterTypeJadeError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDecodingError_lower(_ value: DecodingError) -> RustBuffer { - return FfiConverterTypeDecodingError.lower(value) +public func FfiConverterTypeJadeError_lower(_ value: JadeError) -> RustBuffer { + return FfiConverterTypeJadeError.lower(value) } -extension DecodingError: Equatable, Hashable {} +extension JadeError: Equatable, Hashable {} -extension DecodingError: Codable {} +extension JadeError: Codable {} -extension DecodingError: Foundation.LocalizedError { +extension JadeError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -19367,55 +21200,52 @@ extension DecodingError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * How an application exchanges data with a hardware wallet. - */ -public enum HardwareWalletTransport { +public enum JadeNetwork { - case usb - case bluetooth - case qr + case mainnet + case testnet + case regtest } #if compiler(>=6) -extension HardwareWalletTransport: Sendable {} +extension JadeNetwork: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { - typealias SwiftType = HardwareWalletTransport +public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { + typealias SwiftType = JadeNetwork - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletTransport { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNetwork { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .usb + case 1: return .mainnet - case 2: return .bluetooth + case 2: return .testnet - case 3: return .qr + case 3: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: HardwareWalletTransport, into buf: inout [UInt8]) { + public static func write(_ value: JadeNetwork, into buf: inout [UInt8]) { switch value { - case .usb: + case .mainnet: writeInt(&buf, Int32(1)) - case .bluetooth: + case .testnet: writeInt(&buf, Int32(2)) - case .qr: + case .regtest: writeInt(&buf, Int32(3)) } @@ -19426,21 +21256,21 @@ public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletTransport_lift(_ buf: RustBuffer) throws -> HardwareWalletTransport { - return try FfiConverterTypeHardwareWalletTransport.lift(buf) +public func FfiConverterTypeJadeNetwork_lift(_ buf: RustBuffer) throws -> JadeNetwork { + return try FfiConverterTypeJadeNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletTransport_lower(_ value: HardwareWalletTransport) -> RustBuffer { - return FfiConverterTypeHardwareWalletTransport.lower(value) +public func FfiConverterTypeJadeNetwork_lower(_ value: JadeNetwork) -> RustBuffer { + return FfiConverterTypeJadeNetwork.lower(value) } -extension HardwareWalletTransport: Equatable, Hashable {} +extension JadeNetwork: Equatable, Hashable {} -extension HardwareWalletTransport: Codable {} +extension JadeNetwork: Codable {} @@ -19449,55 +21279,52 @@ extension HardwareWalletTransport: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * A hardware-wallet vendor recognized by Bitkit. - */ -public enum HardwareWalletVendor { +public enum JadePingStatus { - case trezor - case foundation - case blockstream + case idle + case busy + case awaitingUserInput } #if compiler(>=6) -extension HardwareWalletVendor: Sendable {} +extension JadePingStatus: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { - typealias SwiftType = HardwareWalletVendor +public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { + typealias SwiftType = JadePingStatus - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletVendor { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadePingStatus { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .trezor + case 1: return .idle - case 2: return .foundation + case 2: return .busy - case 3: return .blockstream + case 3: return .awaitingUserInput default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: HardwareWalletVendor, into buf: inout [UInt8]) { + public static func write(_ value: JadePingStatus, into buf: inout [UInt8]) { switch value { - case .trezor: + case .idle: writeInt(&buf, Int32(1)) - case .foundation: + case .busy: writeInt(&buf, Int32(2)) - case .blockstream: + case .awaitingUserInput: writeInt(&buf, Int32(3)) } @@ -19508,21 +21335,21 @@ public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletVendor_lift(_ buf: RustBuffer) throws -> HardwareWalletVendor { - return try FfiConverterTypeHardwareWalletVendor.lift(buf) +public func FfiConverterTypeJadePingStatus_lift(_ buf: RustBuffer) throws -> JadePingStatus { + return try FfiConverterTypeJadePingStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletVendor_lower(_ value: HardwareWalletVendor) -> RustBuffer { - return FfiConverterTypeHardwareWalletVendor.lower(value) +public func FfiConverterTypeJadePingStatus_lower(_ value: JadePingStatus) -> RustBuffer { + return FfiConverterTypeJadePingStatus.lower(value) } -extension HardwareWalletVendor: Equatable, Hashable {} +extension JadePingStatus: Equatable, Hashable {} -extension HardwareWalletVendor: Codable {} +extension JadePingStatus: Codable {} @@ -19532,325 +21359,167 @@ extension HardwareWalletVendor: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeAddressVariant { +public enum JadeState { - case pkh - case wpkh - case shWpkh - case tr + case uninit + case unsaved + case locked + case ready + case temp + case unknown } #if compiler(>=6) -extension JadeAddressVariant: Sendable {} +extension JadeState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeAddressVariant: FfiConverterRustBuffer { - typealias SwiftType = JadeAddressVariant +public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { + typealias SwiftType = JadeState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAddressVariant { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .pkh - - case 2: return .wpkh - - case 3: return .shWpkh - - case 4: return .tr - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: JadeAddressVariant, into buf: inout [UInt8]) { - switch value { - - - case .pkh: - writeInt(&buf, Int32(1)) - - - case .wpkh: - writeInt(&buf, Int32(2)) - - - case .shWpkh: - writeInt(&buf, Int32(3)) - + case 1: return .uninit - case .tr: - writeInt(&buf, Int32(4)) + case 2: return .unsaved - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeJadeAddressVariant_lift(_ buf: RustBuffer) throws -> JadeAddressVariant { - return try FfiConverterTypeJadeAddressVariant.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeJadeAddressVariant_lower(_ value: JadeAddressVariant) -> RustBuffer { - return FfiConverterTypeJadeAddressVariant.lower(value) -} - - -extension JadeAddressVariant: Equatable, Hashable {} - -extension JadeAddressVariant: Codable {} - - - - - - - -public enum JadeError: Swift.Error { - - - - case TransportError(errorDetails: String - ) - case DeviceNotFound - case DeviceDisconnected - case DeviceBusy - case NotConnected - case NotInitialized - case ConnectionError(errorDetails: String - ) - case ProtocolError(errorDetails: String - ) - case Timeout - case UserCancelled - case DeviceLocked - case DeviceUninitialized - case InvalidPin - case NetworkMismatch(errorDetails: String - ) - case UnsupportedFirmware(installed: String, required: String - ) - case InvalidPath(errorDetails: String - ) - case InvalidPsbt(errorDetails: String - ) - case PsbtTooLarge(size: UInt64, max: UInt64 - ) - case FingerprintMismatch(device: String, psbt: String - ) - case NothingSigned - case AddressMismatch(expected: String, returned: String - ) - case PinServerError(errorDetails: String - ) - case DeviceError(errorDetails: String - ) - case IoError(errorDetails: String - ) -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { - typealias SwiftType = JadeError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeError { - let variant: Int32 = try readInt(&buf) - switch variant { - + case 3: return .locked - + case 4: return .ready - case 1: return .TransportError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .DeviceNotFound - case 3: return .DeviceDisconnected - case 4: return .DeviceBusy - case 5: return .NotConnected - case 6: return .NotInitialized - case 7: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .ProtocolError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .Timeout - case 10: return .UserCancelled - case 11: return .DeviceLocked - case 12: return .DeviceUninitialized - case 13: return .InvalidPin - case 14: return .NetworkMismatch( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 15: return .UnsupportedFirmware( - installed: try FfiConverterString.read(from: &buf), - required: try FfiConverterString.read(from: &buf) - ) - case 16: return .InvalidPath( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 17: return .InvalidPsbt( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 18: return .PsbtTooLarge( - size: try FfiConverterUInt64.read(from: &buf), - max: try FfiConverterUInt64.read(from: &buf) - ) - case 19: return .FingerprintMismatch( - device: try FfiConverterString.read(from: &buf), - psbt: try FfiConverterString.read(from: &buf) - ) - case 20: return .NothingSigned - case 21: return .AddressMismatch( - expected: try FfiConverterString.read(from: &buf), - returned: try FfiConverterString.read(from: &buf) - ) - case 22: return .PinServerError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 23: return .DeviceError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 24: return .IoError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 5: return .temp + + case 6: return .unknown + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeError, into buf: inout [UInt8]) { + public static func write(_ value: JadeState, into buf: inout [UInt8]) { switch value { - - - - case let .TransportError(errorDetails): + case .uninit: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case .DeviceNotFound: + + case .unsaved: writeInt(&buf, Int32(2)) - case .DeviceDisconnected: + case .locked: writeInt(&buf, Int32(3)) - case .DeviceBusy: + case .ready: writeInt(&buf, Int32(4)) - case .NotConnected: + case .temp: writeInt(&buf, Int32(5)) - case .NotInitialized: + case .unknown: writeInt(&buf, Int32(6)) + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeJadeState_lift(_ buf: RustBuffer) throws -> JadeState { + return try FfiConverterTypeJadeState.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeJadeState_lower(_ value: JadeState) -> RustBuffer { + return FfiConverterTypeJadeState.lower(value) +} + + +extension JadeState: Equatable, Hashable {} + +extension JadeState: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum JadeTransportErrorCode { + + case deviceBusy + case notConnected + case disconnected + case timeout + case permissionDenied +} + + +#if compiler(>=6) +extension JadeTransportErrorCode: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { + typealias SwiftType = JadeTransportErrorCode + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportErrorCode { + let variant: Int32 = try readInt(&buf) + switch variant { - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ProtocolError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .Timeout: - writeInt(&buf, Int32(9)) - - - case .UserCancelled: - writeInt(&buf, Int32(10)) - - - case .DeviceLocked: - writeInt(&buf, Int32(11)) - + case 1: return .deviceBusy - case .DeviceUninitialized: - writeInt(&buf, Int32(12)) + case 2: return .notConnected + case 3: return .disconnected - case .InvalidPin: - writeInt(&buf, Int32(13)) + case 4: return .timeout + case 5: return .permissionDenied - case let .NetworkMismatch(errorDetails): - writeInt(&buf, Int32(14)) - FfiConverterString.write(errorDetails, into: &buf) - + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: JadeTransportErrorCode, into buf: inout [UInt8]) { + switch value { - case let .UnsupportedFirmware(installed,required): - writeInt(&buf, Int32(15)) - FfiConverterString.write(installed, into: &buf) - FfiConverterString.write(required, into: &buf) - - case let .InvalidPath(errorDetails): - writeInt(&buf, Int32(16)) - FfiConverterString.write(errorDetails, into: &buf) - + case .deviceBusy: + writeInt(&buf, Int32(1)) - case let .InvalidPsbt(errorDetails): - writeInt(&buf, Int32(17)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .PsbtTooLarge(size,max): - writeInt(&buf, Int32(18)) - FfiConverterUInt64.write(size, into: &buf) - FfiConverterUInt64.write(max, into: &buf) - + case .notConnected: + writeInt(&buf, Int32(2)) - case let .FingerprintMismatch(device,psbt): - writeInt(&buf, Int32(19)) - FfiConverterString.write(device, into: &buf) - FfiConverterString.write(psbt, into: &buf) - - case .NothingSigned: - writeInt(&buf, Int32(20)) + case .disconnected: + writeInt(&buf, Int32(3)) - case let .AddressMismatch(expected,returned): - writeInt(&buf, Int32(21)) - FfiConverterString.write(expected, into: &buf) - FfiConverterString.write(returned, into: &buf) - + case .timeout: + writeInt(&buf, Int32(4)) - case let .PinServerError(errorDetails): - writeInt(&buf, Int32(22)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .DeviceError(errorDetails): - writeInt(&buf, Int32(23)) - FfiConverterString.write(errorDetails, into: &buf) - + case .permissionDenied: + writeInt(&buf, Int32(5)) - case let .IoError(errorDetails): - writeInt(&buf, Int32(24)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -19859,30 +21528,23 @@ public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeError_lift(_ buf: RustBuffer) throws -> JadeError { - return try FfiConverterTypeJadeError.lift(buf) +public func FfiConverterTypeJadeTransportErrorCode_lift(_ buf: RustBuffer) throws -> JadeTransportErrorCode { + return try FfiConverterTypeJadeTransportErrorCode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeError_lower(_ value: JadeError) -> RustBuffer { - return FfiConverterTypeJadeError.lower(value) +public func FfiConverterTypeJadeTransportErrorCode_lower(_ value: JadeTransportErrorCode) -> RustBuffer { + return FfiConverterTypeJadeTransportErrorCode.lower(value) } -extension JadeError: Equatable, Hashable {} - -extension JadeError: Codable {} - +extension JadeTransportErrorCode: Equatable, Hashable {} +extension JadeTransportErrorCode: Codable {} -extension JadeError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -19890,53 +21552,46 @@ extension JadeError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeNetwork { +public enum JadeTransportKind { - case mainnet - case testnet - case regtest + case bluetooth + case serial } #if compiler(>=6) -extension JadeNetwork: Sendable {} +extension JadeTransportKind: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { - typealias SwiftType = JadeNetwork +public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { + typealias SwiftType = JadeTransportKind - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNetwork { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportKind { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .mainnet - - case 2: return .testnet + case 1: return .bluetooth - case 3: return .regtest + case 2: return .serial default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeNetwork, into buf: inout [UInt8]) { + public static func write(_ value: JadeTransportKind, into buf: inout [UInt8]) { switch value { - case .mainnet: + case .bluetooth: writeInt(&buf, Int32(1)) - case .testnet: + case .serial: writeInt(&buf, Int32(2)) - - case .regtest: - writeInt(&buf, Int32(3)) - } } } @@ -19945,77 +21600,125 @@ public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNetwork_lift(_ buf: RustBuffer) throws -> JadeNetwork { - return try FfiConverterTypeJadeNetwork.lift(buf) +public func FfiConverterTypeJadeTransportKind_lift(_ buf: RustBuffer) throws -> JadeTransportKind { + return try FfiConverterTypeJadeTransportKind.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNetwork_lower(_ value: JadeNetwork) -> RustBuffer { - return FfiConverterTypeJadeNetwork.lower(value) +public func FfiConverterTypeJadeTransportKind_lower(_ value: JadeTransportKind) -> RustBuffer { + return FfiConverterTypeJadeTransportKind.lower(value) } -extension JadeNetwork: Equatable, Hashable {} +extension JadeTransportKind: Equatable, Hashable {} -extension JadeNetwork: Codable {} +extension JadeTransportKind: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadePingStatus { +public enum LnurlError: Swift.Error { + - case idle - case busy - case awaitingUserInput + + case InvalidAddress + case ClientCreationFailed + case RequestFailed + case InvalidResponse + case InvalidAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 + ) + case InvoiceCreationFailed(errorDetails: String + ) + case AmountMismatch(requestedMsats: UInt64, invoiceMsats: UInt64 + ) + case AuthenticationFailed } -#if compiler(>=6) -extension JadePingStatus: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { - typealias SwiftType = JadePingStatus +public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { + typealias SwiftType = LnurlError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadePingStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .idle - - case 2: return .busy - - case 3: return .awaitingUserInput + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidAddress + case 2: return .ClientCreationFailed + case 3: return .RequestFailed + case 4: return .InvalidResponse + case 5: return .InvalidAmount( + amountSatoshis: try FfiConverterUInt64.read(from: &buf), + min: try FfiConverterUInt64.read(from: &buf), + max: try FfiConverterUInt64.read(from: &buf) + ) + case 6: return .InvoiceCreationFailed( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .AmountMismatch( + requestedMsats: try FfiConverterUInt64.read(from: &buf), + invoiceMsats: try FfiConverterUInt64.read(from: &buf) + ) + case 8: return .AuthenticationFailed + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadePingStatus, into buf: inout [UInt8]) { + public static func write(_ value: LnurlError, into buf: inout [UInt8]) { switch value { + + + - case .idle: + case .InvalidAddress: writeInt(&buf, Int32(1)) - case .busy: + case .ClientCreationFailed: writeInt(&buf, Int32(2)) - case .awaitingUserInput: + case .RequestFailed: writeInt(&buf, Int32(3)) + + case .InvalidResponse: + writeInt(&buf, Int32(4)) + + + case let .InvalidAmount(amountSatoshis,min,max): + writeInt(&buf, Int32(5)) + FfiConverterUInt64.write(amountSatoshis, into: &buf) + FfiConverterUInt64.write(min, into: &buf) + FfiConverterUInt64.write(max, into: &buf) + + + case let .InvoiceCreationFailed(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .AmountMismatch(requestedMsats,invoiceMsats): + writeInt(&buf, Int32(7)) + FfiConverterUInt64.write(requestedMsats, into: &buf) + FfiConverterUInt64.write(invoiceMsats, into: &buf) + + + case .AuthenticationFailed: + writeInt(&buf, Int32(8)) + } } } @@ -20024,98 +21727,91 @@ public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadePingStatus_lift(_ buf: RustBuffer) throws -> JadePingStatus { - return try FfiConverterTypeJadePingStatus.lift(buf) +public func FfiConverterTypeLnurlError_lift(_ buf: RustBuffer) throws -> LnurlError { + return try FfiConverterTypeLnurlError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadePingStatus_lower(_ value: JadePingStatus) -> RustBuffer { - return FfiConverterTypeJadePingStatus.lower(value) +public func FfiConverterTypeLnurlError_lower(_ value: LnurlError) -> RustBuffer { + return FfiConverterTypeLnurlError.lower(value) } -extension JadePingStatus: Equatable, Hashable {} +extension LnurlError: Equatable, Hashable {} + +extension LnurlError: Codable {} -extension JadePingStatus: Codable {} +extension LnurlError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeState { +public enum ManualRefundStateEnum { - case uninit - case unsaved - case locked - case ready - case temp - case unknown + case created + case approved + case rejected + case sent } #if compiler(>=6) -extension JadeState: Sendable {} +extension ManualRefundStateEnum: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { - typealias SwiftType = JadeState +public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { + typealias SwiftType = ManualRefundStateEnum - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ManualRefundStateEnum { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .uninit - - case 2: return .unsaved - - case 3: return .locked + case 1: return .created - case 4: return .ready + case 2: return .approved - case 5: return .temp + case 3: return .rejected - case 6: return .unknown + case 4: return .sent default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeState, into buf: inout [UInt8]) { + public static func write(_ value: ManualRefundStateEnum, into buf: inout [UInt8]) { switch value { - case .uninit: + case .created: writeInt(&buf, Int32(1)) - case .unsaved: + case .approved: writeInt(&buf, Int32(2)) - case .locked: + case .rejected: writeInt(&buf, Int32(3)) - case .ready: + case .sent: writeInt(&buf, Int32(4)) - - case .temp: - writeInt(&buf, Int32(5)) - - - case .unknown: - writeInt(&buf, Int32(6)) - } } } @@ -20124,21 +21820,21 @@ public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeState_lift(_ buf: RustBuffer) throws -> JadeState { - return try FfiConverterTypeJadeState.lift(buf) +public func FfiConverterTypeManualRefundStateEnum_lift(_ buf: RustBuffer) throws -> ManualRefundStateEnum { + return try FfiConverterTypeManualRefundStateEnum.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeState_lower(_ value: JadeState) -> RustBuffer { - return FfiConverterTypeJadeState.lower(value) +public func FfiConverterTypeManualRefundStateEnum_lower(_ value: ManualRefundStateEnum) -> RustBuffer { + return FfiConverterTypeManualRefundStateEnum.lower(value) } -extension JadeState: Equatable, Hashable {} +extension ManualRefundStateEnum: Equatable, Hashable {} -extension JadeState: Codable {} +extension ManualRefundStateEnum: Codable {} @@ -20148,65 +21844,80 @@ extension JadeState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeTransportErrorCode { +public enum Network { - case deviceBusy - case notConnected - case disconnected - case timeout - case permissionDenied + /** + * Mainnet Bitcoin. + */ + case bitcoin + /** + * Bitcoin's testnet network. + */ + case testnet + /** + * Bitcoin's testnet4 network. + */ + case testnet4 + /** + * Bitcoin's signet network. + */ + case signet + /** + * Bitcoin's regtest network. + */ + case regtest } #if compiler(>=6) -extension JadeTransportErrorCode: Sendable {} +extension Network: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { - typealias SwiftType = JadeTransportErrorCode +public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { + typealias SwiftType = Network - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportErrorCode { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Network { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .deviceBusy + case 1: return .bitcoin - case 2: return .notConnected + case 2: return .testnet - case 3: return .disconnected + case 3: return .testnet4 - case 4: return .timeout + case 4: return .signet - case 5: return .permissionDenied + case 5: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeTransportErrorCode, into buf: inout [UInt8]) { + public static func write(_ value: Network, into buf: inout [UInt8]) { switch value { - case .deviceBusy: + case .bitcoin: writeInt(&buf, Int32(1)) - case .notConnected: + case .testnet: writeInt(&buf, Int32(2)) - case .disconnected: + case .testnet4: writeInt(&buf, Int32(3)) - case .timeout: + case .signet: writeInt(&buf, Int32(4)) - case .permissionDenied: + case .regtest: writeInt(&buf, Int32(5)) } @@ -20217,21 +21928,21 @@ public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportErrorCode_lift(_ buf: RustBuffer) throws -> JadeTransportErrorCode { - return try FfiConverterTypeJadeTransportErrorCode.lift(buf) +public func FfiConverterTypeNetwork_lift(_ buf: RustBuffer) throws -> Network { + return try FfiConverterTypeNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportErrorCode_lower(_ value: JadeTransportErrorCode) -> RustBuffer { - return FfiConverterTypeJadeTransportErrorCode.lower(value) +public func FfiConverterTypeNetwork_lower(_ value: Network) -> RustBuffer { + return FfiConverterTypeNetwork.lower(value) } -extension JadeTransportErrorCode: Equatable, Hashable {} +extension Network: Equatable, Hashable {} -extension JadeTransportErrorCode: Codable {} +extension Network: Codable {} @@ -20241,46 +21952,60 @@ extension JadeTransportErrorCode: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeTransportKind { +public enum NetworkType { - case bluetooth - case serial + case bitcoin + case testnet + case regtest + case signet } #if compiler(>=6) -extension JadeTransportKind: Sendable {} +extension NetworkType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { - typealias SwiftType = JadeTransportKind +public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { + typealias SwiftType = NetworkType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportKind { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NetworkType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bluetooth + case 1: return .bitcoin - case 2: return .serial + case 2: return .testnet + + case 3: return .regtest + + case 4: return .signet default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeTransportKind, into buf: inout [UInt8]) { + public static func write(_ value: NetworkType, into buf: inout [UInt8]) { switch value { - case .bluetooth: + case .bitcoin: writeInt(&buf, Int32(1)) - case .serial: + case .testnet: writeInt(&buf, Int32(2)) + + case .regtest: + writeInt(&buf, Int32(3)) + + + case .signet: + writeInt(&buf, Int32(4)) + } } } @@ -20289,21 +22014,21 @@ public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportKind_lift(_ buf: RustBuffer) throws -> JadeTransportKind { - return try FfiConverterTypeJadeTransportKind.lift(buf) +public func FfiConverterTypeNetworkType_lift(_ buf: RustBuffer) throws -> NetworkType { + return try FfiConverterTypeNetworkType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportKind_lower(_ value: JadeTransportKind) -> RustBuffer { - return FfiConverterTypeJadeTransportKind.lower(value) +public func FfiConverterTypeNetworkType_lower(_ value: NetworkType) -> RustBuffer { + return FfiConverterTypeNetworkType.lower(value) } -extension JadeTransportKind: Equatable, Hashable {} +extension NetworkType: Equatable, Hashable {} -extension JadeTransportKind: Codable {} +extension NetworkType: Codable {} @@ -20311,103 +22036,47 @@ extension JadeTransportKind: Codable {} -public enum LnurlError: Swift.Error { +public enum OnchainError: Swift.Error { - case InvalidAddress - case ClientCreationFailed - case RequestFailed - case InvalidResponse - case InvalidAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 - ) - case InvoiceCreationFailed(errorDetails: String - ) - case AmountMismatch(requestedMsats: UInt64, invoiceMsats: UInt64 + case InvalidExtendedPublicKey(errorDetails: String ) - case AuthenticationFailed } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { - typealias SwiftType = LnurlError +public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { + typealias SwiftType = OnchainError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidAddress - case 2: return .ClientCreationFailed - case 3: return .RequestFailed - case 4: return .InvalidResponse - case 5: return .InvalidAmount( - amountSatoshis: try FfiConverterUInt64.read(from: &buf), - min: try FfiConverterUInt64.read(from: &buf), - max: try FfiConverterUInt64.read(from: &buf) - ) - case 6: return .InvoiceCreationFailed( + case 1: return .InvalidExtendedPublicKey( errorDetails: try FfiConverterString.read(from: &buf) ) - case 7: return .AmountMismatch( - requestedMsats: try FfiConverterUInt64.read(from: &buf), - invoiceMsats: try FfiConverterUInt64.read(from: &buf) - ) - case 8: return .AuthenticationFailed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: LnurlError, into buf: inout [UInt8]) { + public static func write(_ value: OnchainError, into buf: inout [UInt8]) { switch value { - case .InvalidAddress: + case let .InvalidExtendedPublicKey(errorDetails): writeInt(&buf, Int32(1)) - - - case .ClientCreationFailed: - writeInt(&buf, Int32(2)) - - - case .RequestFailed: - writeInt(&buf, Int32(3)) - - - case .InvalidResponse: - writeInt(&buf, Int32(4)) - - - case let .InvalidAmount(amountSatoshis,min,max): - writeInt(&buf, Int32(5)) - FfiConverterUInt64.write(amountSatoshis, into: &buf) - FfiConverterUInt64.write(min, into: &buf) - FfiConverterUInt64.write(max, into: &buf) - - - case let .InvoiceCreationFailed(errorDetails): - writeInt(&buf, Int32(6)) FfiConverterString.write(errorDetails, into: &buf) - - case let .AmountMismatch(requestedMsats,invoiceMsats): - writeInt(&buf, Int32(7)) - FfiConverterUInt64.write(requestedMsats, into: &buf) - FfiConverterUInt64.write(invoiceMsats, into: &buf) - - - case .AuthenticationFailed: - writeInt(&buf, Int32(8)) - } } } @@ -20416,26 +22085,26 @@ public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlError_lift(_ buf: RustBuffer) throws -> LnurlError { - return try FfiConverterTypeLnurlError.lift(buf) +public func FfiConverterTypeOnchainError_lift(_ buf: RustBuffer) throws -> OnchainError { + return try FfiConverterTypeOnchainError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlError_lower(_ value: LnurlError) -> RustBuffer { - return FfiConverterTypeLnurlError.lower(value) +public func FfiConverterTypeOnchainError_lower(_ value: OnchainError) -> RustBuffer { + return FfiConverterTypeOnchainError.lower(value) } -extension LnurlError: Equatable, Hashable {} +extension OnchainError: Equatable, Hashable {} -extension LnurlError: Codable {} +extension OnchainError: Codable {} -extension LnurlError: Foundation.LocalizedError { +extension OnchainError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -20447,58 +22116,73 @@ extension LnurlError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum ManualRefundStateEnum { +public enum PassphraseResponse { - case created - case approved - case rejected - case sent + /** + * User cancelled — aborts the pending operation. + */ + case cancel + /** + * Standard wallet — no passphrase, equivalent to `Some("")` on the device. + */ + case standard + /** + * Hidden wallet — derived from the passphrase entered on the host. + */ + case hidden(value: String + ) + /** + * Enter the passphrase on the Trezor device itself instead of on the host. + */ + case onDevice } #if compiler(>=6) -extension ManualRefundStateEnum: Sendable {} +extension PassphraseResponse: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { - typealias SwiftType = ManualRefundStateEnum +public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { + typealias SwiftType = PassphraseResponse - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ManualRefundStateEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassphraseResponse { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created + case 1: return .cancel - case 2: return .approved + case 2: return .standard - case 3: return .rejected + case 3: return .hidden(value: try FfiConverterString.read(from: &buf) + ) - case 4: return .sent + case 4: return .onDevice default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ManualRefundStateEnum, into buf: inout [UInt8]) { + public static func write(_ value: PassphraseResponse, into buf: inout [UInt8]) { switch value { - case .created: + case .cancel: writeInt(&buf, Int32(1)) - case .approved: + case .standard: writeInt(&buf, Int32(2)) - case .rejected: + case let .hidden(value): writeInt(&buf, Int32(3)) + FfiConverterString.write(value, into: &buf) + - - case .sent: + case .onDevice: writeInt(&buf, Int32(4)) } @@ -20509,106 +22193,77 @@ public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeManualRefundStateEnum_lift(_ buf: RustBuffer) throws -> ManualRefundStateEnum { - return try FfiConverterTypeManualRefundStateEnum.lift(buf) +public func FfiConverterTypePassphraseResponse_lift(_ buf: RustBuffer) throws -> PassphraseResponse { + return try FfiConverterTypePassphraseResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeManualRefundStateEnum_lower(_ value: ManualRefundStateEnum) -> RustBuffer { - return FfiConverterTypeManualRefundStateEnum.lower(value) +public func FfiConverterTypePassphraseResponse_lower(_ value: PassphraseResponse) -> RustBuffer { + return FfiConverterTypePassphraseResponse.lower(value) } -extension ManualRefundStateEnum: Equatable, Hashable {} - -extension ManualRefundStateEnum: Codable {} - - - - - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +extension PassphraseResponse: Equatable, Hashable {} -public enum Network { - - /** - * Mainnet Bitcoin. - */ - case bitcoin - /** - * Bitcoin's testnet network. - */ - case testnet - /** - * Bitcoin's testnet4 network. - */ - case testnet4 - /** - * Bitcoin's signet network. - */ - case signet - /** - * Bitcoin's regtest network. - */ - case regtest +extension PassphraseResponse: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum PaymentState { + + case pending + case succeeded + case failed } #if compiler(>=6) -extension Network: Sendable {} +extension PaymentState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { - typealias SwiftType = Network +public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { + typealias SwiftType = PaymentState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Network { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .testnet4 + case 1: return .pending - case 4: return .signet + case 2: return .succeeded - case 5: return .regtest + case 3: return .failed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Network, into buf: inout [UInt8]) { + public static func write(_ value: PaymentState, into buf: inout [UInt8]) { switch value { - case .bitcoin: + case .pending: writeInt(&buf, Int32(1)) - case .testnet: + case .succeeded: writeInt(&buf, Int32(2)) - case .testnet4: + case .failed: writeInt(&buf, Int32(3)) - - case .signet: - writeInt(&buf, Int32(4)) - - - case .regtest: - writeInt(&buf, Int32(5)) - } } } @@ -20617,21 +22272,21 @@ public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetwork_lift(_ buf: RustBuffer) throws -> Network { - return try FfiConverterTypeNetwork.lift(buf) +public func FfiConverterTypePaymentState_lift(_ buf: RustBuffer) throws -> PaymentState { + return try FfiConverterTypePaymentState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetwork_lower(_ value: Network) -> RustBuffer { - return FfiConverterTypeNetwork.lower(value) +public func FfiConverterTypePaymentState_lower(_ value: PaymentState) -> RustBuffer { + return FfiConverterTypePaymentState.lower(value) } -extension Network: Equatable, Hashable {} +extension PaymentState: Equatable, Hashable {} -extension Network: Codable {} +extension PaymentState: Codable {} @@ -20641,60 +22296,46 @@ extension Network: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum NetworkType { +public enum PaymentType { - case bitcoin - case testnet - case regtest - case signet + case sent + case received } #if compiler(>=6) -extension NetworkType: Sendable {} +extension PaymentType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { - typealias SwiftType = NetworkType +public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { + typealias SwiftType = PaymentType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NetworkType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .regtest + case 1: return .sent - case 4: return .signet + case 2: return .received default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: NetworkType, into buf: inout [UInt8]) { + public static func write(_ value: PaymentType, into buf: inout [UInt8]) { switch value { - case .bitcoin: + case .sent: writeInt(&buf, Int32(1)) - case .testnet: + case .received: writeInt(&buf, Int32(2)) - - case .regtest: - writeInt(&buf, Int32(3)) - - - case .signet: - writeInt(&buf, Int32(4)) - } } } @@ -20703,21 +22344,21 @@ public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetworkType_lift(_ buf: RustBuffer) throws -> NetworkType { - return try FfiConverterTypeNetworkType.lift(buf) +public func FfiConverterTypePaymentType_lift(_ buf: RustBuffer) throws -> PaymentType { + return try FfiConverterTypePaymentType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetworkType_lower(_ value: NetworkType) -> RustBuffer { - return FfiConverterTypeNetworkType.lower(value) +public func FfiConverterTypePaymentType_lower(_ value: PaymentType) -> RustBuffer { + return FfiConverterTypePaymentType.lower(value) } -extension NetworkType: Equatable, Hashable {} +extension PaymentType: Equatable, Hashable {} -extension NetworkType: Codable {} +extension PaymentType: Codable {} @@ -20725,11 +22366,19 @@ extension NetworkType: Codable {} -public enum OnchainError: Swift.Error { +public enum PsbtCompletionError: Swift.Error { - case InvalidExtendedPublicKey(errorDetails: String + case InvalidPsbt(reason: String + ) + case CombineFailed(reason: String + ) + case FinalizationFailed(reason: String + ) + case VerificationFailed(reason: String + ) + case ExtractionFailed(reason: String ) } @@ -20737,34 +22386,66 @@ public enum OnchainError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { - typealias SwiftType = OnchainError +public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { + typealias SwiftType = PsbtCompletionError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PsbtCompletionError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidExtendedPublicKey( - errorDetails: try FfiConverterString.read(from: &buf) + case 1: return .InvalidPsbt( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .CombineFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .FinalizationFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 4: return .VerificationFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .ExtractionFailed( + reason: try FfiConverterString.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: OnchainError, into buf: inout [UInt8]) { + public static func write(_ value: PsbtCompletionError, into buf: inout [UInt8]) { switch value { - case let .InvalidExtendedPublicKey(errorDetails): + case let .InvalidPsbt(reason): writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) + FfiConverterString.write(reason, into: &buf) + + + case let .CombineFailed(reason): + writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + + + case let .FinalizationFailed(reason): + writeInt(&buf, Int32(3)) + FfiConverterString.write(reason, into: &buf) + + + case let .VerificationFailed(reason): + writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + + + case let .ExtractionFailed(reason): + writeInt(&buf, Int32(5)) + FfiConverterString.write(reason, into: &buf) } } @@ -20774,26 +22455,26 @@ public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnchainError_lift(_ buf: RustBuffer) throws -> OnchainError { - return try FfiConverterTypeOnchainError.lift(buf) +public func FfiConverterTypePsbtCompletionError_lift(_ buf: RustBuffer) throws -> PsbtCompletionError { + return try FfiConverterTypePsbtCompletionError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnchainError_lower(_ value: OnchainError) -> RustBuffer { - return FfiConverterTypeOnchainError.lower(value) +public func FfiConverterTypePsbtCompletionError_lower(_ value: PsbtCompletionError) -> RustBuffer { + return FfiConverterTypePsbtCompletionError.lower(value) } -extension OnchainError: Equatable, Hashable {} +extension PsbtCompletionError: Equatable, Hashable {} -extension OnchainError: Codable {} +extension PsbtCompletionError: Codable {} -extension OnchainError: Foundation.LocalizedError { +extension PsbtCompletionError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -20804,76 +22485,50 @@ extension OnchainError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The type of a `pubkyauth://` deep-link flow. + */ -public enum PassphraseResponse { +public enum PubkyAuthKind { - /** - * User cancelled — aborts the pending operation. - */ - case cancel - /** - * Standard wallet — no passphrase, equivalent to `Some("")` on the device. - */ - case standard - /** - * Hidden wallet — derived from the passphrase entered on the host. - */ - case hidden(value: String - ) - /** - * Enter the passphrase on the Trezor device itself instead of on the host. - */ - case onDevice + case signin + case signup } #if compiler(>=6) -extension PassphraseResponse: Sendable {} +extension PubkyAuthKind: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { - typealias SwiftType = PassphraseResponse +public struct FfiConverterTypePubkyAuthKind: FfiConverterRustBuffer { + typealias SwiftType = PubkyAuthKind - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassphraseResponse { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthKind { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .cancel - - case 2: return .standard - - case 3: return .hidden(value: try FfiConverterString.read(from: &buf) - ) + case 1: return .signin - case 4: return .onDevice + case 2: return .signup default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PassphraseResponse, into buf: inout [UInt8]) { + public static func write(_ value: PubkyAuthKind, into buf: inout [UInt8]) { switch value { - case .cancel: + case .signin: writeInt(&buf, Int32(1)) - case .standard: + case .signup: writeInt(&buf, Int32(2)) - - case let .hidden(value): - writeInt(&buf, Int32(3)) - FfiConverterString.write(value, into: &buf) - - - case .onDevice: - writeInt(&buf, Int32(4)) - } } } @@ -20882,77 +22537,141 @@ public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassphraseResponse_lift(_ buf: RustBuffer) throws -> PassphraseResponse { - return try FfiConverterTypePassphraseResponse.lift(buf) +public func FfiConverterTypePubkyAuthKind_lift(_ buf: RustBuffer) throws -> PubkyAuthKind { + return try FfiConverterTypePubkyAuthKind.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassphraseResponse_lower(_ value: PassphraseResponse) -> RustBuffer { - return FfiConverterTypePassphraseResponse.lower(value) +public func FfiConverterTypePubkyAuthKind_lower(_ value: PubkyAuthKind) -> RustBuffer { + return FfiConverterTypePubkyAuthKind.lower(value) } -extension PassphraseResponse: Equatable, Hashable {} +extension PubkyAuthKind: Equatable, Hashable {} -extension PassphraseResponse: Codable {} +extension PubkyAuthKind: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum PaymentState { +public enum PubkyError: Swift.Error { + - case pending - case succeeded - case failed + + case InvalidCapabilities(reason: String + ) + case AuthFailed(reason: String + ) + case NoActiveFlow + case ResolutionFailed(reason: String + ) + case FetchFailed(reason: String + ) + case ProfileNotFound + case ProfileParseFailed(reason: String + ) + case KeyError(reason: String + ) + case WriteFailed(reason: String + ) } -#if compiler(>=6) -extension PaymentState: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { - typealias SwiftType = PaymentState +public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { + typealias SwiftType = PubkyError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .pending - - case 2: return .succeeded - - case 3: return .failed + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidCapabilities( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .AuthFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .NoActiveFlow + case 4: return .ResolutionFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .FetchFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 6: return .ProfileNotFound + case 7: return .ProfileParseFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 8: return .KeyError( + reason: try FfiConverterString.read(from: &buf) + ) + case 9: return .WriteFailed( + reason: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PaymentState, into buf: inout [UInt8]) { + public static func write(_ value: PubkyError, into buf: inout [UInt8]) { switch value { + + - case .pending: - writeInt(&buf, Int32(1)) + case let .InvalidCapabilities(reason): + writeInt(&buf, Int32(1)) + FfiConverterString.write(reason, into: &buf) + - case .succeeded: + case let .AuthFailed(reason): writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + - - case .failed: + case .NoActiveFlow: writeInt(&buf, Int32(3)) + + case let .ResolutionFailed(reason): + writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + + + case let .FetchFailed(reason): + writeInt(&buf, Int32(5)) + FfiConverterString.write(reason, into: &buf) + + + case .ProfileNotFound: + writeInt(&buf, Int32(6)) + + + case let .ProfileParseFailed(reason): + writeInt(&buf, Int32(7)) + FfiConverterString.write(reason, into: &buf) + + + case let .KeyError(reason): + writeInt(&buf, Int32(8)) + FfiConverterString.write(reason, into: &buf) + + + case let .WriteFailed(reason): + writeInt(&buf, Int32(9)) + FfiConverterString.write(reason, into: &buf) + } } } @@ -20961,70 +22680,165 @@ public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentState_lift(_ buf: RustBuffer) throws -> PaymentState { - return try FfiConverterTypePaymentState.lift(buf) +public func FfiConverterTypePubkyError_lift(_ buf: RustBuffer) throws -> PubkyError { + return try FfiConverterTypePubkyError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentState_lower(_ value: PaymentState) -> RustBuffer { - return FfiConverterTypePaymentState.lower(value) +public func FfiConverterTypePubkyError_lower(_ value: PubkyError) -> RustBuffer { + return FfiConverterTypePubkyError.lower(value) } -extension PaymentState: Equatable, Hashable {} +extension PubkyError: Equatable, Hashable {} + +extension PubkyError: Codable {} -extension PaymentState: Codable {} +extension PubkyError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum PaymentType { +public enum Scanner { - case sent - case received + case onChain(invoice: OnChainInvoice + ) + case lightning(invoice: LightningInvoice + ) + case pubkyAuth(data: String + ) + case lnurlChannel(data: LnurlChannelData + ) + case lnurlAuth(data: LnurlAuthData + ) + case lnurlWithdraw(data: LnurlWithdrawData + ) + case lnurlAddress(data: LnurlAddressData + ) + case lnurlPay(data: LnurlPayData + ) + case nodeId(url: String, network: NetworkType + ) + case gift(code: String, amount: UInt64 + ) } #if compiler(>=6) -extension PaymentType: Sendable {} +extension Scanner: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { - typealias SwiftType = PaymentType +public struct FfiConverterTypeScanner: FfiConverterRustBuffer { + typealias SwiftType = Scanner - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Scanner { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .sent + case 1: return .onChain(invoice: try FfiConverterTypeOnChainInvoice.read(from: &buf) + ) - case 2: return .received + case 2: return .lightning(invoice: try FfiConverterTypeLightningInvoice.read(from: &buf) + ) + + case 3: return .pubkyAuth(data: try FfiConverterString.read(from: &buf) + ) + + case 4: return .lnurlChannel(data: try FfiConverterTypeLnurlChannelData.read(from: &buf) + ) + + case 5: return .lnurlAuth(data: try FfiConverterTypeLnurlAuthData.read(from: &buf) + ) + + case 6: return .lnurlWithdraw(data: try FfiConverterTypeLnurlWithdrawData.read(from: &buf) + ) + + case 7: return .lnurlAddress(data: try FfiConverterTypeLnurlAddressData.read(from: &buf) + ) + + case 8: return .lnurlPay(data: try FfiConverterTypeLnurlPayData.read(from: &buf) + ) + + case 9: return .nodeId(url: try FfiConverterString.read(from: &buf), network: try FfiConverterTypeNetworkType.read(from: &buf) + ) + + case 10: return .gift(code: try FfiConverterString.read(from: &buf), amount: try FfiConverterUInt64.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PaymentType, into buf: inout [UInt8]) { + public static func write(_ value: Scanner, into buf: inout [UInt8]) { switch value { - case .sent: + case let .onChain(invoice): writeInt(&buf, Int32(1)) + FfiConverterTypeOnChainInvoice.write(invoice, into: &buf) + - - case .received: + case let .lightning(invoice): writeInt(&buf, Int32(2)) + FfiConverterTypeLightningInvoice.write(invoice, into: &buf) + + + case let .pubkyAuth(data): + writeInt(&buf, Int32(3)) + FfiConverterString.write(data, into: &buf) + + + case let .lnurlChannel(data): + writeInt(&buf, Int32(4)) + FfiConverterTypeLnurlChannelData.write(data, into: &buf) + + + case let .lnurlAuth(data): + writeInt(&buf, Int32(5)) + FfiConverterTypeLnurlAuthData.write(data, into: &buf) + + + case let .lnurlWithdraw(data): + writeInt(&buf, Int32(6)) + FfiConverterTypeLnurlWithdrawData.write(data, into: &buf) + + + case let .lnurlAddress(data): + writeInt(&buf, Int32(7)) + FfiConverterTypeLnurlAddressData.write(data, into: &buf) + + + case let .lnurlPay(data): + writeInt(&buf, Int32(8)) + FfiConverterTypeLnurlPayData.write(data, into: &buf) + + + case let .nodeId(url,network): + writeInt(&buf, Int32(9)) + FfiConverterString.write(url, into: &buf) + FfiConverterTypeNetworkType.write(network, into: &buf) + + case let .gift(code,amount): + writeInt(&buf, Int32(10)) + FfiConverterString.write(code, into: &buf) + FfiConverterUInt64.write(amount, into: &buf) + } } } @@ -21033,21 +22847,21 @@ public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentType_lift(_ buf: RustBuffer) throws -> PaymentType { - return try FfiConverterTypePaymentType.lift(buf) +public func FfiConverterTypeScanner_lift(_ buf: RustBuffer) throws -> Scanner { + return try FfiConverterTypeScanner.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentType_lower(_ value: PaymentType) -> RustBuffer { - return FfiConverterTypePaymentType.lower(value) +public func FfiConverterTypeScanner_lower(_ value: Scanner) -> RustBuffer { + return FfiConverterTypeScanner.lower(value) } -extension PaymentType: Equatable, Hashable {} +extension Scanner: Equatable, Hashable {} -extension PaymentType: Codable {} +extension Scanner: Codable {} @@ -21055,87 +22869,55 @@ extension PaymentType: Codable {} -public enum PsbtCompletionError: Swift.Error { +public enum SeedQrError: Swift.Error { - case InvalidPsbt(reason: String - ) - case CombineFailed(reason: String - ) - case FinalizationFailed(reason: String - ) - case VerificationFailed(reason: String - ) - case ExtractionFailed(reason: String - ) + case InvalidStandardPayload + case InvalidCompactPayload + case InvalidMnemonic } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { - typealias SwiftType = PsbtCompletionError +public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { + typealias SwiftType = SeedQrError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PsbtCompletionError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SeedQrError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidPsbt( - reason: try FfiConverterString.read(from: &buf) - ) - case 2: return .CombineFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 3: return .FinalizationFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 4: return .VerificationFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 5: return .ExtractionFailed( - reason: try FfiConverterString.read(from: &buf) - ) + case 1: return .InvalidStandardPayload + case 2: return .InvalidCompactPayload + case 3: return .InvalidMnemonic default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PsbtCompletionError, into buf: inout [UInt8]) { + public static func write(_ value: SeedQrError, into buf: inout [UInt8]) { switch value { - case let .InvalidPsbt(reason): + case .InvalidStandardPayload: writeInt(&buf, Int32(1)) - FfiConverterString.write(reason, into: &buf) - - case let .CombineFailed(reason): + + case .InvalidCompactPayload: writeInt(&buf, Int32(2)) - FfiConverterString.write(reason, into: &buf) - - case let .FinalizationFailed(reason): - writeInt(&buf, Int32(3)) - FfiConverterString.write(reason, into: &buf) - - case let .VerificationFailed(reason): - writeInt(&buf, Int32(4)) - FfiConverterString.write(reason, into: &buf) - + case .InvalidMnemonic: + writeInt(&buf, Int32(3)) - case let .ExtractionFailed(reason): - writeInt(&buf, Int32(5)) - FfiConverterString.write(reason, into: &buf) - } } } @@ -21144,26 +22926,26 @@ public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePsbtCompletionError_lift(_ buf: RustBuffer) throws -> PsbtCompletionError { - return try FfiConverterTypePsbtCompletionError.lift(buf) +public func FfiConverterTypeSeedQrError_lift(_ buf: RustBuffer) throws -> SeedQrError { + return try FfiConverterTypeSeedQrError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePsbtCompletionError_lower(_ value: PsbtCompletionError) -> RustBuffer { - return FfiConverterTypePsbtCompletionError.lower(value) +public func FfiConverterTypeSeedQrError_lower(_ value: SeedQrError) -> RustBuffer { + return FfiConverterTypeSeedQrError.lower(value) } -extension PsbtCompletionError: Equatable, Hashable {} +extension SeedQrError: Equatable, Hashable {} -extension PsbtCompletionError: Codable {} +extension SeedQrError: Codable {} -extension PsbtCompletionError: Foundation.LocalizedError { +extension SeedQrError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -21174,48 +22956,45 @@ extension PsbtCompletionError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * The type of a `pubkyauth://` deep-link flow. - */ -public enum PubkyAuthKind { +public enum SortDirection { - case signin - case signup + case asc + case desc } #if compiler(>=6) -extension PubkyAuthKind: Sendable {} +extension SortDirection: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyAuthKind: FfiConverterRustBuffer { - typealias SwiftType = PubkyAuthKind +public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { + typealias SwiftType = SortDirection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthKind { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SortDirection { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .signin + case 1: return .asc - case 2: return .signup + case 2: return .desc default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PubkyAuthKind, into buf: inout [UInt8]) { + public static func write(_ value: SortDirection, into buf: inout [UInt8]) { switch value { - case .signin: + case .asc: writeInt(&buf, Int32(1)) - case .signup: + case .desc: writeInt(&buf, Int32(2)) } @@ -21226,21 +23005,21 @@ public struct FfiConverterTypePubkyAuthKind: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuthKind_lift(_ buf: RustBuffer) throws -> PubkyAuthKind { - return try FfiConverterTypePubkyAuthKind.lift(buf) +public func FfiConverterTypeSortDirection_lift(_ buf: RustBuffer) throws -> SortDirection { + return try FfiConverterTypeSortDirection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyAuthKind_lower(_ value: PubkyAuthKind) -> RustBuffer { - return FfiConverterTypePubkyAuthKind.lower(value) +public func FfiConverterTypeSortDirection_lower(_ value: SortDirection) -> RustBuffer { + return FfiConverterTypeSortDirection.lower(value) } -extension PubkyAuthKind: Equatable, Hashable {} +extension SortDirection: Equatable, Hashable {} -extension PubkyAuthKind: Codable {} +extension SortDirection: Codable {} @@ -21248,119 +23027,167 @@ extension PubkyAuthKind: Codable {} -public enum PubkyError: Swift.Error { +public enum SweepError: Swift.Error { - case InvalidCapabilities(reason: String - ) - case AuthFailed(reason: String - ) - case NoActiveFlow - case ResolutionFailed(reason: String - ) - case FetchFailed(reason: String - ) - case ProfileNotFound - case ProfileParseFailed(reason: String - ) - case KeyError(reason: String - ) - case WriteFailed(reason: String + case SweepFailed(String ) + case NoUtxosFound + case InvalidMnemonic } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { - typealias SwiftType = PubkyError +public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { + typealias SwiftType = SweepError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidCapabilities( - reason: try FfiConverterString.read(from: &buf) - ) - case 2: return .AuthFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 3: return .NoActiveFlow - case 4: return .ResolutionFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 5: return .FetchFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 6: return .ProfileNotFound - case 7: return .ProfileParseFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 8: return .KeyError( - reason: try FfiConverterString.read(from: &buf) - ) - case 9: return .WriteFailed( - reason: try FfiConverterString.read(from: &buf) + case 1: return .SweepFailed( + try FfiConverterString.read(from: &buf) ) + case 2: return .NoUtxosFound + case 3: return .InvalidMnemonic + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: SweepError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .SweepFailed(v1): + writeInt(&buf, Int32(1)) + FfiConverterString.write(v1, into: &buf) + + + case .NoUtxosFound: + writeInt(&buf, Int32(2)) + + + case .InvalidMnemonic: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSweepError_lift(_ buf: RustBuffer) throws -> SweepError { + return try FfiConverterTypeSweepError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeSweepError_lower(_ value: SweepError) -> RustBuffer { + return FfiConverterTypeSweepError.lower(value) +} + + +extension SweepError: Equatable, Hashable {} + +extension SweepError: Codable {} + + + + +extension SweepError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Bitcoin network / coin type for Trezor operations. + */ + +public enum TrezorCoinType { + + /** + * Bitcoin mainnet + */ + case bitcoin + /** + * Bitcoin testnet + */ + case testnet + /** + * Bitcoin signet (treated as testnet by the device) + */ + case signet + /** + * Bitcoin regtest + */ + case regtest +} + + +#if compiler(>=6) +extension TrezorCoinType: Sendable {} +#endif - default: throw UniffiInternalError.unexpectedEnumCase +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTrezorCoinType: FfiConverterRustBuffer { + typealias SwiftType = TrezorCoinType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCoinType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .bitcoin + + case 2: return .testnet + + case 3: return .signet + + case 4: return .regtest + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PubkyError, into buf: inout [UInt8]) { + public static func write(_ value: TrezorCoinType, into buf: inout [UInt8]) { switch value { - - - - case let .InvalidCapabilities(reason): + case .bitcoin: writeInt(&buf, Int32(1)) - FfiConverterString.write(reason, into: &buf) - - case let .AuthFailed(reason): + + case .testnet: writeInt(&buf, Int32(2)) - FfiConverterString.write(reason, into: &buf) - - case .NoActiveFlow: + + case .signet: writeInt(&buf, Int32(3)) - case let .ResolutionFailed(reason): + case .regtest: writeInt(&buf, Int32(4)) - FfiConverterString.write(reason, into: &buf) - - - case let .FetchFailed(reason): - writeInt(&buf, Int32(5)) - FfiConverterString.write(reason, into: &buf) - - - case .ProfileNotFound: - writeInt(&buf, Int32(6)) - - - case let .ProfileParseFailed(reason): - writeInt(&buf, Int32(7)) - FfiConverterString.write(reason, into: &buf) - - - case let .KeyError(reason): - writeInt(&buf, Int32(8)) - FfiConverterString.write(reason, into: &buf) - - case let .WriteFailed(reason): - writeInt(&buf, Int32(9)) - FfiConverterString.write(reason, into: &buf) - } } } @@ -21369,244 +23196,283 @@ public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyError_lift(_ buf: RustBuffer) throws -> PubkyError { - return try FfiConverterTypePubkyError.lift(buf) +public func FfiConverterTypeTrezorCoinType_lift(_ buf: RustBuffer) throws -> TrezorCoinType { + return try FfiConverterTypeTrezorCoinType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyError_lower(_ value: PubkyError) -> RustBuffer { - return FfiConverterTypePubkyError.lower(value) +public func FfiConverterTypeTrezorCoinType_lower(_ value: TrezorCoinType) -> RustBuffer { + return FfiConverterTypeTrezorCoinType.lower(value) } -extension PubkyError: Equatable, Hashable {} - -extension PubkyError: Codable {} +extension TrezorCoinType: Equatable, Hashable {} +extension TrezorCoinType: Codable {} -extension PubkyError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Trezor-related errors exposed via FFI. + */ +public enum TrezorError: Swift.Error { -public enum Scanner { - case onChain(invoice: OnChainInvoice - ) - case lightning(invoice: LightningInvoice - ) - case pubkyAuth(data: String + + /** + * Transport layer error (USB/Bluetooth communication) + */ + case TransportError(errorDetails: String ) - case lnurlChannel(data: LnurlChannelData + /** + * No Trezor device found + */ + case DeviceNotFound + /** + * Device disconnected during operation + */ + case DeviceDisconnected + /** + * Device is busy and the caller should back off before retrying + */ + case DeviceBusy + /** + * Connection error + */ + case ConnectionError(errorDetails: String ) - case lnurlAuth(data: LnurlAuthData + /** + * Protocol error (encoding/decoding) + */ + case ProtocolError(errorDetails: String ) - case lnurlWithdraw(data: LnurlWithdrawData + /** + * Pairing required for Bluetooth connection + */ + case PairingRequired + /** + * Pairing failed + */ + case PairingFailed(errorDetails: String ) - case lnurlAddress(data: LnurlAddressData + /** + * PIN is required + */ + case PinRequired + /** + * PIN entry cancelled + */ + case PinCancelled + /** + * Invalid PIN entered + */ + case InvalidPin + /** + * Passphrase is required + */ + case PassphraseRequired + /** + * Passphrase entry cancelled + */ + case PassphraseCancelled + /** + * Action cancelled by user on device + */ + case UserCancelled + /** + * Operation timed out + */ + case Timeout + /** + * Invalid derivation path + */ + case InvalidPath(errorDetails: String ) - case lnurlPay(data: LnurlPayData + /** + * Device returned an error + */ + case DeviceError(errorDetails: String ) - case nodeId(url: String, network: NetworkType + /** + * Trezor manager not initialized + */ + case NotInitialized + /** + * No device connected + */ + case NotConnected + /** + * Session error + */ + case SessionError(errorDetails: String ) - case gift(code: String, amount: UInt64 + /** + * IO error + */ + case IoError(errorDetails: String ) } -#if compiler(>=6) -extension Scanner: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeScanner: FfiConverterRustBuffer { - typealias SwiftType = Scanner +public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { + typealias SwiftType = TrezorError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Scanner { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .onChain(invoice: try FfiConverterTypeOnChainInvoice.read(from: &buf) - ) - - case 2: return .lightning(invoice: try FfiConverterTypeLightningInvoice.read(from: &buf) - ) - - case 3: return .pubkyAuth(data: try FfiConverterString.read(from: &buf) - ) - - case 4: return .lnurlChannel(data: try FfiConverterTypeLnurlChannelData.read(from: &buf) - ) - - case 5: return .lnurlAuth(data: try FfiConverterTypeLnurlAuthData.read(from: &buf) - ) - - case 6: return .lnurlWithdraw(data: try FfiConverterTypeLnurlWithdrawData.read(from: &buf) - ) - - case 7: return .lnurlAddress(data: try FfiConverterTypeLnurlAddressData.read(from: &buf) - ) - - case 8: return .lnurlPay(data: try FfiConverterTypeLnurlPayData.read(from: &buf) - ) - - case 9: return .nodeId(url: try FfiConverterString.read(from: &buf), network: try FfiConverterTypeNetworkType.read(from: &buf) - ) - - case 10: return .gift(code: try FfiConverterString.read(from: &buf), amount: try FfiConverterUInt64.read(from: &buf) - ) + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .TransportError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .DeviceNotFound + case 3: return .DeviceDisconnected + case 4: return .DeviceBusy + case 5: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .ProtocolError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .PairingRequired + case 8: return .PairingFailed( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .PinRequired + case 10: return .PinCancelled + case 11: return .InvalidPin + case 12: return .PassphraseRequired + case 13: return .PassphraseCancelled + case 14: return .UserCancelled + case 15: return .Timeout + case 16: return .InvalidPath( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 17: return .DeviceError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 18: return .NotInitialized + case 19: return .NotConnected + case 20: return .SessionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 21: return .IoError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Scanner, into buf: inout [UInt8]) { + public static func write(_ value: TrezorError, into buf: inout [UInt8]) { switch value { + + + - case let .onChain(invoice): + case let .TransportError(errorDetails): writeInt(&buf, Int32(1)) - FfiConverterTypeOnChainInvoice.write(invoice, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lightning(invoice): + case .DeviceNotFound: writeInt(&buf, Int32(2)) - FfiConverterTypeLightningInvoice.write(invoice, into: &buf) - - case let .pubkyAuth(data): + + case .DeviceDisconnected: writeInt(&buf, Int32(3)) - FfiConverterString.write(data, into: &buf) - - case let .lnurlChannel(data): + + case .DeviceBusy: writeInt(&buf, Int32(4)) - FfiConverterTypeLnurlChannelData.write(data, into: &buf) - - case let .lnurlAuth(data): + + case let .ConnectionError(errorDetails): writeInt(&buf, Int32(5)) - FfiConverterTypeLnurlAuthData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lnurlWithdraw(data): + case let .ProtocolError(errorDetails): writeInt(&buf, Int32(6)) - FfiConverterTypeLnurlWithdrawData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lnurlAddress(data): + case .PairingRequired: writeInt(&buf, Int32(7)) - FfiConverterTypeLnurlAddressData.write(data, into: &buf) - - case let .lnurlPay(data): + + case let .PairingFailed(errorDetails): writeInt(&buf, Int32(8)) - FfiConverterTypeLnurlPayData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .nodeId(url,network): + case .PinRequired: writeInt(&buf, Int32(9)) - FfiConverterString.write(url, into: &buf) - FfiConverterTypeNetworkType.write(network, into: &buf) - - case let .gift(code,amount): + + case .PinCancelled: writeInt(&buf, Int32(10)) - FfiConverterString.write(code, into: &buf) - FfiConverterUInt64.write(amount, into: &buf) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeScanner_lift(_ buf: RustBuffer) throws -> Scanner { - return try FfiConverterTypeScanner.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeScanner_lower(_ value: Scanner) -> RustBuffer { - return FfiConverterTypeScanner.lower(value) -} - - -extension Scanner: Equatable, Hashable {} - -extension Scanner: Codable {} - - - - - - - -public enum SeedQrError: Swift.Error { - - - - case InvalidStandardPayload - case InvalidCompactPayload - case InvalidMnemonic -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { - typealias SwiftType = SeedQrError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SeedQrError { - let variant: Int32 = try readInt(&buf) - switch variant { - - - case 1: return .InvalidStandardPayload - case 2: return .InvalidCompactPayload - case 3: return .InvalidMnemonic - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: SeedQrError, into buf: inout [UInt8]) { - switch value { - + case .InvalidPin: + writeInt(&buf, Int32(11)) - + + case .PassphraseRequired: + writeInt(&buf, Int32(12)) - case .InvalidStandardPayload: - writeInt(&buf, Int32(1)) + case .PassphraseCancelled: + writeInt(&buf, Int32(13)) - case .InvalidCompactPayload: - writeInt(&buf, Int32(2)) + case .UserCancelled: + writeInt(&buf, Int32(14)) - case .InvalidMnemonic: - writeInt(&buf, Int32(3)) + case .Timeout: + writeInt(&buf, Int32(15)) + + + case let .InvalidPath(errorDetails): + writeInt(&buf, Int32(16)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DeviceError(errorDetails): + writeInt(&buf, Int32(17)) + FfiConverterString.write(errorDetails, into: &buf) + + + case .NotInitialized: + writeInt(&buf, Int32(18)) + + case .NotConnected: + writeInt(&buf, Int32(19)) + + + case let .SessionError(errorDetails): + writeInt(&buf, Int32(20)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .IoError(errorDetails): + writeInt(&buf, Int32(21)) + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -21615,26 +23481,26 @@ public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSeedQrError_lift(_ buf: RustBuffer) throws -> SeedQrError { - return try FfiConverterTypeSeedQrError.lift(buf) +public func FfiConverterTypeTrezorError_lift(_ buf: RustBuffer) throws -> TrezorError { + return try FfiConverterTypeTrezorError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSeedQrError_lower(_ value: SeedQrError) -> RustBuffer { - return FfiConverterTypeSeedQrError.lower(value) +public func FfiConverterTypeTrezorError_lower(_ value: TrezorError) -> RustBuffer { + return FfiConverterTypeTrezorError.lower(value) } -extension SeedQrError: Equatable, Hashable {} +extension TrezorError: Equatable, Hashable {} -extension SeedQrError: Codable {} +extension TrezorError: Codable {} -extension SeedQrError: Foundation.LocalizedError { +extension TrezorError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -21645,46 +23511,95 @@ extension SeedQrError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Script types for address derivation. + */ -public enum SortDirection { +public enum TrezorScriptType { - case asc - case desc + /** + * P2PKH (legacy) + */ + case spendAddress + /** + * P2SH-P2WPKH (nested SegWit) + */ + case spendP2shWitness + /** + * P2WPKH (native SegWit) + */ + case spendWitness + /** + * P2TR (Taproot) + */ + case spendTaproot + /** + * P2SH multisig + */ + case spendMultisig + /** + * External/watch-only input (not signed by device) + */ + case external } #if compiler(>=6) -extension SortDirection: Sendable {} +extension TrezorScriptType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { - typealias SwiftType = SortDirection +public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { + typealias SwiftType = TrezorScriptType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SortDirection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorScriptType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .asc + case 1: return .spendAddress - case 2: return .desc + case 2: return .spendP2shWitness + + case 3: return .spendWitness + + case 4: return .spendTaproot + + case 5: return .spendMultisig + + case 6: return .external default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: SortDirection, into buf: inout [UInt8]) { + public static func write(_ value: TrezorScriptType, into buf: inout [UInt8]) { switch value { - case .asc: + case .spendAddress: writeInt(&buf, Int32(1)) - case .desc: - writeInt(&buf, Int32(2)) + case .spendP2shWitness: + writeInt(&buf, Int32(2)) + + + case .spendWitness: + writeInt(&buf, Int32(3)) + + + case .spendTaproot: + writeInt(&buf, Int32(4)) + + + case .spendMultisig: + writeInt(&buf, Int32(5)) + + + case .external: + writeInt(&buf, Int32(6)) } } @@ -21694,80 +23609,68 @@ public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSortDirection_lift(_ buf: RustBuffer) throws -> SortDirection { - return try FfiConverterTypeSortDirection.lift(buf) +public func FfiConverterTypeTrezorScriptType_lift(_ buf: RustBuffer) throws -> TrezorScriptType { + return try FfiConverterTypeTrezorScriptType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSortDirection_lower(_ value: SortDirection) -> RustBuffer { - return FfiConverterTypeSortDirection.lower(value) +public func FfiConverterTypeTrezorScriptType_lower(_ value: TrezorScriptType) -> RustBuffer { + return FfiConverterTypeTrezorScriptType.lower(value) } -extension SortDirection: Equatable, Hashable {} - -extension SortDirection: Codable {} +extension TrezorScriptType: Equatable, Hashable {} +extension TrezorScriptType: Codable {} -public enum SweepError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Structured transport error code returned by native callback operations. + */ +public enum TrezorTransportErrorCode { - - case SweepFailed(String - ) - case NoUtxosFound - case InvalidMnemonic + /** + * Device is busy and the caller should back off before retrying. + */ + case deviceBusy } +#if compiler(>=6) +extension TrezorTransportErrorCode: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { - typealias SwiftType = SweepError +public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { + typealias SwiftType = TrezorTransportErrorCode - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportErrorCode { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .deviceBusy - case 1: return .SweepFailed( - try FfiConverterString.read(from: &buf) - ) - case 2: return .NoUtxosFound - case 3: return .InvalidMnemonic - - default: throw UniffiInternalError.unexpectedEnumCase + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: SweepError, into buf: inout [UInt8]) { + public static func write(_ value: TrezorTransportErrorCode, into buf: inout [UInt8]) { switch value { - - - - case let .SweepFailed(v1): + case .deviceBusy: writeInt(&buf, Int32(1)) - FfiConverterString.write(v1, into: &buf) - - - case .NoUtxosFound: - writeInt(&buf, Int32(2)) - - - case .InvalidMnemonic: - writeInt(&buf, Int32(3)) } } @@ -21777,30 +23680,23 @@ public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepError_lift(_ buf: RustBuffer) throws -> SweepError { - return try FfiConverterTypeSweepError.lift(buf) +public func FfiConverterTypeTrezorTransportErrorCode_lift(_ buf: RustBuffer) throws -> TrezorTransportErrorCode { + return try FfiConverterTypeTrezorTransportErrorCode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepError_lower(_ value: SweepError) -> RustBuffer { - return FfiConverterTypeSweepError.lower(value) +public func FfiConverterTypeTrezorTransportErrorCode_lower(_ value: TrezorTransportErrorCode) -> RustBuffer { + return FfiConverterTypeTrezorTransportErrorCode.lower(value) } -extension SweepError: Equatable, Hashable {} - -extension SweepError: Codable {} - +extension TrezorTransportErrorCode: Equatable, Hashable {} +extension TrezorTransportErrorCode: Codable {} -extension SweepError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -21808,360 +23704,146 @@ extension SweepError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Bitcoin network / coin type for Trezor operations. + * Transport type for Trezor devices. */ -public enum TrezorCoinType { +public enum TrezorTransportType { /** - * Bitcoin mainnet - */ - case bitcoin - /** - * Bitcoin testnet - */ - case testnet - /** - * Bitcoin signet (treated as testnet by the device) + * USB connection */ - case signet + case usb /** - * Bitcoin regtest + * Bluetooth connection */ - case regtest + case bluetooth } #if compiler(>=6) -extension TrezorCoinType: Sendable {} +extension TrezorTransportType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorCoinType: FfiConverterRustBuffer { - typealias SwiftType = TrezorCoinType +public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { + typealias SwiftType = TrezorTransportType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCoinType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .signet + case 1: return .usb - case 4: return .regtest + case 2: return .bluetooth default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorCoinType, into buf: inout [UInt8]) { + public static func write(_ value: TrezorTransportType, into buf: inout [UInt8]) { switch value { - case .bitcoin: + case .usb: writeInt(&buf, Int32(1)) - case .testnet: + case .bluetooth: writeInt(&buf, Int32(2)) - - case .signet: - writeInt(&buf, Int32(3)) - - - case .regtest: - writeInt(&buf, Int32(4)) - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeTrezorCoinType_lift(_ buf: RustBuffer) throws -> TrezorCoinType { - return try FfiConverterTypeTrezorCoinType.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeTrezorCoinType_lower(_ value: TrezorCoinType) -> RustBuffer { - return FfiConverterTypeTrezorCoinType.lower(value) -} - - -extension TrezorCoinType: Equatable, Hashable {} - -extension TrezorCoinType: Codable {} - - - - - - - -/** - * Trezor-related errors exposed via FFI. - */ -public enum TrezorError: Swift.Error { - - - - /** - * Transport layer error (USB/Bluetooth communication) - */ - case TransportError(errorDetails: String - ) - /** - * No Trezor device found - */ - case DeviceNotFound - /** - * Device disconnected during operation - */ - case DeviceDisconnected - /** - * Device is busy and the caller should back off before retrying - */ - case DeviceBusy - /** - * Connection error - */ - case ConnectionError(errorDetails: String - ) - /** - * Protocol error (encoding/decoding) - */ - case ProtocolError(errorDetails: String - ) - /** - * Pairing required for Bluetooth connection - */ - case PairingRequired - /** - * Pairing failed - */ - case PairingFailed(errorDetails: String - ) - /** - * PIN is required - */ - case PinRequired - /** - * PIN entry cancelled - */ - case PinCancelled - /** - * Invalid PIN entered - */ - case InvalidPin - /** - * Passphrase is required - */ - case PassphraseRequired - /** - * Passphrase entry cancelled - */ - case PassphraseCancelled - /** - * Action cancelled by user on device - */ - case UserCancelled - /** - * Operation timed out - */ - case Timeout - /** - * Invalid derivation path - */ - case InvalidPath(errorDetails: String - ) - /** - * Device returned an error - */ - case DeviceError(errorDetails: String - ) - /** - * Trezor manager not initialized - */ - case NotInitialized + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTrezorTransportType_lift(_ buf: RustBuffer) throws -> TrezorTransportType { + return try FfiConverterTypeTrezorTransportType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeTrezorTransportType_lower(_ value: TrezorTransportType) -> RustBuffer { + return FfiConverterTypeTrezorTransportType.lower(value) +} + + +extension TrezorTransportType: Equatable, Hashable {} + +extension TrezorTransportType: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Transaction direction from the wallet's perspective. + */ + +public enum TxDirection { + /** - * No device connected + * Wallet sent funds to an external address */ - case NotConnected + case sent /** - * Session error + * Wallet received funds from an external source */ - case SessionError(errorDetails: String - ) + case received /** - * IO error + * Wallet sent funds to itself (e.g. consolidation, change-only) */ - case IoError(errorDetails: String - ) + case selfTransfer } +#if compiler(>=6) +extension TxDirection: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { - typealias SwiftType = TrezorError +public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { + typealias SwiftType = TxDirection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDirection { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .sent - case 1: return .TransportError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .DeviceNotFound - case 3: return .DeviceDisconnected - case 4: return .DeviceBusy - case 5: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .ProtocolError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .PairingRequired - case 8: return .PairingFailed( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .PinRequired - case 10: return .PinCancelled - case 11: return .InvalidPin - case 12: return .PassphraseRequired - case 13: return .PassphraseCancelled - case 14: return .UserCancelled - case 15: return .Timeout - case 16: return .InvalidPath( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 17: return .DeviceError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 18: return .NotInitialized - case 19: return .NotConnected - case 20: return .SessionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 21: return .IoError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .received + + case 3: return .selfTransfer + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorError, into buf: inout [UInt8]) { + public static func write(_ value: TxDirection, into buf: inout [UInt8]) { switch value { - - - - case let .TransportError(errorDetails): + case .sent: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case .DeviceNotFound: + + case .received: writeInt(&buf, Int32(2)) - case .DeviceDisconnected: + case .selfTransfer: writeInt(&buf, Int32(3)) - - case .DeviceBusy: - writeInt(&buf, Int32(4)) - - - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ProtocolError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .PairingRequired: - writeInt(&buf, Int32(7)) - - - case let .PairingFailed(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .PinRequired: - writeInt(&buf, Int32(9)) - - - case .PinCancelled: - writeInt(&buf, Int32(10)) - - - case .InvalidPin: - writeInt(&buf, Int32(11)) - - - case .PassphraseRequired: - writeInt(&buf, Int32(12)) - - - case .PassphraseCancelled: - writeInt(&buf, Int32(13)) - - - case .UserCancelled: - writeInt(&buf, Int32(14)) - - - case .Timeout: - writeInt(&buf, Int32(15)) - - - case let .InvalidPath(errorDetails): - writeInt(&buf, Int32(16)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .DeviceError(errorDetails): - writeInt(&buf, Int32(17)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .NotInitialized: - writeInt(&buf, Int32(18)) - - - case .NotConnected: - writeInt(&buf, Int32(19)) - - - case let .SessionError(errorDetails): - writeInt(&buf, Int32(20)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .IoError(errorDetails): - writeInt(&buf, Int32(21)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -22170,126 +23852,109 @@ public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorError_lift(_ buf: RustBuffer) throws -> TrezorError { - return try FfiConverterTypeTrezorError.lift(buf) +public func FfiConverterTypeTxDirection_lift(_ buf: RustBuffer) throws -> TxDirection { + return try FfiConverterTypeTxDirection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorError_lower(_ value: TrezorError) -> RustBuffer { - return FfiConverterTypeTrezorError.lower(value) +public func FfiConverterTypeTxDirection_lower(_ value: TxDirection) -> RustBuffer { + return FfiConverterTypeTxDirection.lower(value) } -extension TrezorError: Equatable, Hashable {} - -extension TrezorError: Codable {} +extension TxDirection: Equatable, Hashable {} +extension TxDirection: Codable {} -extension TrezorError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Script types for address derivation. - */ +public enum UrError: Swift.Error { -public enum TrezorScriptType { - /** - * P2PKH (legacy) - */ - case spendAddress - /** - * P2SH-P2WPKH (nested SegWit) - */ - case spendP2shWitness - /** - * P2WPKH (native SegWit) - */ - case spendWitness - /** - * P2TR (Taproot) - */ - case spendTaproot - /** - * P2SH multisig - */ - case spendMultisig - /** - * External/watch-only input (not signed by device) - */ - case external + + case InvalidUr(reason: String + ) + case TooLarge(reason: String + ) + case InvalidPayload(reason: String + ) + case InvalidPsbt(reason: String + ) + case InvalidPassportExport(reason: String + ) } -#if compiler(>=6) -extension TrezorScriptType: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { - typealias SwiftType = TrezorScriptType +public struct FfiConverterTypeUrError: FfiConverterRustBuffer { + typealias SwiftType = UrError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorScriptType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .spendAddress - - case 2: return .spendP2shWitness - - case 3: return .spendWitness - - case 4: return .spendTaproot - - case 5: return .spendMultisig - - case 6: return .external + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidUr( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .TooLarge( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidPayload( + reason: try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidPsbt( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .InvalidPassportExport( + reason: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorScriptType, into buf: inout [UInt8]) { + public static func write(_ value: UrError, into buf: inout [UInt8]) { switch value { + + - case .spendAddress: - writeInt(&buf, Int32(1)) + case let .InvalidUr(reason): + writeInt(&buf, Int32(1)) + FfiConverterString.write(reason, into: &buf) + - case .spendP2shWitness: + case let .TooLarge(reason): writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + - - case .spendWitness: + case let .InvalidPayload(reason): writeInt(&buf, Int32(3)) + FfiConverterString.write(reason, into: &buf) + - - case .spendTaproot: + case let .InvalidPsbt(reason): writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + - - case .spendMultisig: + case let .InvalidPassportExport(reason): writeInt(&buf, Int32(5)) - - - case .external: - writeInt(&buf, Int32(6)) - + FfiConverterString.write(reason, into: &buf) + } } } @@ -22298,69 +23963,106 @@ public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorScriptType_lift(_ buf: RustBuffer) throws -> TrezorScriptType { - return try FfiConverterTypeTrezorScriptType.lift(buf) +public func FfiConverterTypeUrError_lift(_ buf: RustBuffer) throws -> UrError { + return try FfiConverterTypeUrError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorScriptType_lower(_ value: TrezorScriptType) -> RustBuffer { - return FfiConverterTypeTrezorScriptType.lower(value) +public func FfiConverterTypeUrError_lower(_ value: UrError) -> RustBuffer { + return FfiConverterTypeUrError.lower(value) } -extension TrezorScriptType: Equatable, Hashable {} +extension UrError: Equatable, Hashable {} + +extension UrError: Codable {} -extension TrezorScriptType: Codable {} +extension UrError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Structured transport error code returned by native callback operations. + * A completely decoded UR payload. */ -public enum TrezorTransportErrorCode { +public enum UrPayload { /** - * Device is busy and the caller should back off before retrying. + * The byte string wrapped by a `bytes` registry item. */ - case deviceBusy + case bytes(data: Data + ) + /** + * A `crypto-psbt` registry item, returned in Bitkit's usual base64 form. + */ + case cryptoPsbt(psbt: String + ) + /** + * An uninterpreted UR registry item. + */ + case cbor(urType: String, cbor: Data + ) } #if compiler(>=6) -extension TrezorTransportErrorCode: Sendable {} +extension UrPayload: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { - typealias SwiftType = TrezorTransportErrorCode +public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { + typealias SwiftType = UrPayload - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportErrorCode { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrPayload { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .deviceBusy + case 1: return .bytes(data: try FfiConverterData.read(from: &buf) + ) + + case 2: return .cryptoPsbt(psbt: try FfiConverterString.read(from: &buf) + ) + + case 3: return .cbor(urType: try FfiConverterString.read(from: &buf), cbor: try FfiConverterData.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorTransportErrorCode, into buf: inout [UInt8]) { + public static func write(_ value: UrPayload, into buf: inout [UInt8]) { switch value { - case .deviceBusy: + case let .bytes(data): writeInt(&buf, Int32(1)) + FfiConverterData.write(data, into: &buf) + + + case let .cryptoPsbt(psbt): + writeInt(&buf, Int32(2)) + FfiConverterString.write(psbt, into: &buf) + + case let .cbor(urType,cbor): + writeInt(&buf, Int32(3)) + FfiConverterString.write(urType, into: &buf) + FfiConverterData.write(cbor, into: &buf) + } } } @@ -22369,21 +24071,21 @@ public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportErrorCode_lift(_ buf: RustBuffer) throws -> TrezorTransportErrorCode { - return try FfiConverterTypeTrezorTransportErrorCode.lift(buf) +public func FfiConverterTypeUrPayload_lift(_ buf: RustBuffer) throws -> UrPayload { + return try FfiConverterTypeUrPayload.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportErrorCode_lower(_ value: TrezorTransportErrorCode) -> RustBuffer { - return FfiConverterTypeTrezorTransportErrorCode.lower(value) +public func FfiConverterTypeUrPayload_lower(_ value: UrPayload) -> RustBuffer { + return FfiConverterTypeUrPayload.lower(value) } -extension TrezorTransportErrorCode: Equatable, Hashable {} +extension UrPayload: Equatable, Hashable {} -extension TrezorTransportErrorCode: Codable {} +extension UrPayload: Codable {} @@ -22392,54 +24094,45 @@ extension TrezorTransportErrorCode: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Transport type for Trezor devices. - */ -public enum TrezorTransportType { +public enum UsdtDepositNetwork { - /** - * USB connection - */ - case usb - /** - * Bluetooth connection - */ - case bluetooth + case ethereum + case tron } #if compiler(>=6) -extension TrezorTransportType: Sendable {} +extension UsdtDepositNetwork: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { - typealias SwiftType = TrezorTransportType +public struct FfiConverterTypeUsdtDepositNetwork: FfiConverterRustBuffer { + typealias SwiftType = UsdtDepositNetwork - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositNetwork { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .usb + case 1: return .ethereum - case 2: return .bluetooth + case 2: return .tron default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorTransportType, into buf: inout [UInt8]) { + public static func write(_ value: UsdtDepositNetwork, into buf: inout [UInt8]) { switch value { - case .usb: + case .ethereum: writeInt(&buf, Int32(1)) - case .bluetooth: + case .tron: writeInt(&buf, Int32(2)) } @@ -22450,21 +24143,21 @@ public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportType_lift(_ buf: RustBuffer) throws -> TrezorTransportType { - return try FfiConverterTypeTrezorTransportType.lift(buf) +public func FfiConverterTypeUsdtDepositNetwork_lift(_ buf: RustBuffer) throws -> UsdtDepositNetwork { + return try FfiConverterTypeUsdtDepositNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportType_lower(_ value: TrezorTransportType) -> RustBuffer { - return FfiConverterTypeTrezorTransportType.lower(value) +public func FfiConverterTypeUsdtDepositNetwork_lower(_ value: UsdtDepositNetwork) -> RustBuffer { + return FfiConverterTypeUsdtDepositNetwork.lower(value) } -extension TrezorTransportType: Equatable, Hashable {} +extension UsdtDepositNetwork: Equatable, Hashable {} -extension TrezorTransportType: Codable {} +extension UsdtDepositNetwork: Codable {} @@ -22473,66 +24166,68 @@ extension TrezorTransportType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Transaction direction from the wallet's perspective. - */ -public enum TxDirection { +public enum UsdtDestination { - /** - * Wallet sent funds to an external address - */ - case sent - /** - * Wallet received funds from an external source - */ - case received - /** - * Wallet sent funds to itself (e.g. consolidation, change-only) - */ - case selfTransfer + case stable + case ethereum + case arbitrum + case polygon + case plasma } #if compiler(>=6) -extension TxDirection: Sendable {} +extension UsdtDestination: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { - typealias SwiftType = TxDirection +public struct FfiConverterTypeUsdtDestination: FfiConverterRustBuffer { + typealias SwiftType = UsdtDestination - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDirection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDestination { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .sent + case 1: return .stable - case 2: return .received + case 2: return .ethereum - case 3: return .selfTransfer + case 3: return .arbitrum + + case 4: return .polygon + + case 5: return .plasma default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TxDirection, into buf: inout [UInt8]) { + public static func write(_ value: UsdtDestination, into buf: inout [UInt8]) { switch value { - case .sent: + case .stable: writeInt(&buf, Int32(1)) - case .received: + case .ethereum: writeInt(&buf, Int32(2)) - case .selfTransfer: + case .arbitrum: writeInt(&buf, Int32(3)) + + case .polygon: + writeInt(&buf, Int32(4)) + + + case .plasma: + writeInt(&buf, Int32(5)) + } } } @@ -22541,21 +24236,21 @@ public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDirection_lift(_ buf: RustBuffer) throws -> TxDirection { - return try FfiConverterTypeTxDirection.lift(buf) +public func FfiConverterTypeUsdtDestination_lift(_ buf: RustBuffer) throws -> UsdtDestination { + return try FfiConverterTypeUsdtDestination.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDirection_lower(_ value: TxDirection) -> RustBuffer { - return FfiConverterTypeTxDirection.lower(value) +public func FfiConverterTypeUsdtDestination_lower(_ value: UsdtDestination) -> RustBuffer { + return FfiConverterTypeUsdtDestination.lower(value) } -extension TxDirection: Equatable, Hashable {} +extension UsdtDestination: Equatable, Hashable {} -extension TxDirection: Codable {} +extension UsdtDestination: Codable {} @@ -22563,87 +24258,147 @@ extension TxDirection: Codable {} -public enum UrError: Swift.Error { +public enum UsdtError: Swift.Error { - case InvalidUr(reason: String - ) - case TooLarge(reason: String - ) - case InvalidPayload(reason: String - ) - case InvalidPsbt(reason: String + case InvalidAmount + case InvalidAddress + case WrongNetwork + case InvalidCredentials + case UnsupportedDelegation + case InsufficientBalance + case QuoteExpired + case PendingTransfer + case UnsupportedRoute + case DepositNeedsAttention + case NotConfigured + case NetworkUnavailable + case RateLimited + case LogRangeTooLarge + case TransactionRejected(reason: String ) - case InvalidPassportExport(reason: String + case Storage(reason: String ) + case InvalidResponse } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrError: FfiConverterRustBuffer { - typealias SwiftType = UrError +public struct FfiConverterTypeUsdtError: FfiConverterRustBuffer { + typealias SwiftType = UsdtError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidUr( - reason: try FfiConverterString.read(from: &buf) - ) - case 2: return .TooLarge( - reason: try FfiConverterString.read(from: &buf) - ) - case 3: return .InvalidPayload( + case 1: return .InvalidAmount + case 2: return .InvalidAddress + case 3: return .WrongNetwork + case 4: return .InvalidCredentials + case 5: return .UnsupportedDelegation + case 6: return .InsufficientBalance + case 7: return .QuoteExpired + case 8: return .PendingTransfer + case 9: return .UnsupportedRoute + case 10: return .DepositNeedsAttention + case 11: return .NotConfigured + case 12: return .NetworkUnavailable + case 13: return .RateLimited + case 14: return .LogRangeTooLarge + case 15: return .TransactionRejected( reason: try FfiConverterString.read(from: &buf) ) - case 4: return .InvalidPsbt( - reason: try FfiConverterString.read(from: &buf) - ) - case 5: return .InvalidPassportExport( + case 16: return .Storage( reason: try FfiConverterString.read(from: &buf) ) + case 17: return .InvalidResponse default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: UrError, into buf: inout [UInt8]) { + public static func write(_ value: UsdtError, into buf: inout [UInt8]) { switch value { - case let .InvalidUr(reason): + case .InvalidAmount: writeInt(&buf, Int32(1)) - FfiConverterString.write(reason, into: &buf) - - case let .TooLarge(reason): + + case .InvalidAddress: writeInt(&buf, Int32(2)) - FfiConverterString.write(reason, into: &buf) - - case let .InvalidPayload(reason): + + case .WrongNetwork: writeInt(&buf, Int32(3)) - FfiConverterString.write(reason, into: &buf) - - case let .InvalidPsbt(reason): + + case .InvalidCredentials: writeInt(&buf, Int32(4)) + + + case .UnsupportedDelegation: + writeInt(&buf, Int32(5)) + + + case .InsufficientBalance: + writeInt(&buf, Int32(6)) + + + case .QuoteExpired: + writeInt(&buf, Int32(7)) + + + case .PendingTransfer: + writeInt(&buf, Int32(8)) + + + case .UnsupportedRoute: + writeInt(&buf, Int32(9)) + + + case .DepositNeedsAttention: + writeInt(&buf, Int32(10)) + + + case .NotConfigured: + writeInt(&buf, Int32(11)) + + + case .NetworkUnavailable: + writeInt(&buf, Int32(12)) + + + case .RateLimited: + writeInt(&buf, Int32(13)) + + + case .LogRangeTooLarge: + writeInt(&buf, Int32(14)) + + + case let .TransactionRejected(reason): + writeInt(&buf, Int32(15)) FfiConverterString.write(reason, into: &buf) - case let .InvalidPassportExport(reason): - writeInt(&buf, Int32(5)) + case let .Storage(reason): + writeInt(&buf, Int32(16)) FfiConverterString.write(reason, into: &buf) + + case .InvalidResponse: + writeInt(&buf, Int32(17)) + } } } @@ -22652,26 +24407,26 @@ public struct FfiConverterTypeUrError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrError_lift(_ buf: RustBuffer) throws -> UrError { - return try FfiConverterTypeUrError.lift(buf) +public func FfiConverterTypeUsdtError_lift(_ buf: RustBuffer) throws -> UsdtError { + return try FfiConverterTypeUsdtError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrError_lower(_ value: UrError) -> RustBuffer { - return FfiConverterTypeUrError.lower(value) +public func FfiConverterTypeUsdtError_lower(_ value: UsdtError) -> RustBuffer { + return FfiConverterTypeUsdtError.lower(value) } -extension UrError: Equatable, Hashable {} +extension UsdtError: Equatable, Hashable {} -extension UrError: Codable {} +extension UsdtError: Codable {} -extension UrError: Foundation.LocalizedError { +extension UsdtError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -22682,76 +24437,75 @@ extension UrError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * A completely decoded UR payload. - */ -public enum UrPayload { +public enum UsdtTransferStatus { - /** - * The byte string wrapped by a `bytes` registry item. - */ - case bytes(data: Data - ) - /** - * A `crypto-psbt` registry item, returned in Bitkit's usual base64 form. - */ - case cryptoPsbt(psbt: String - ) - /** - * An uninterpreted UR registry item. - */ - case cbor(urType: String, cbor: Data - ) + case pending + case confirmed + case failed + case bridging + case bridgeNeedsAttention + case replaced } #if compiler(>=6) -extension UrPayload: Sendable {} +extension UsdtTransferStatus: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { - typealias SwiftType = UrPayload +public struct FfiConverterTypeUsdtTransferStatus: FfiConverterRustBuffer { + typealias SwiftType = UsdtTransferStatus - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrPayload { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtTransferStatus { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bytes(data: try FfiConverterData.read(from: &buf) - ) + case 1: return .pending - case 2: return .cryptoPsbt(psbt: try FfiConverterString.read(from: &buf) - ) + case 2: return .confirmed - case 3: return .cbor(urType: try FfiConverterString.read(from: &buf), cbor: try FfiConverterData.read(from: &buf) - ) + case 3: return .failed + + case 4: return .bridging + + case 5: return .bridgeNeedsAttention + + case 6: return .replaced default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: UrPayload, into buf: inout [UInt8]) { + public static func write(_ value: UsdtTransferStatus, into buf: inout [UInt8]) { switch value { - case let .bytes(data): + case .pending: writeInt(&buf, Int32(1)) - FfiConverterData.write(data, into: &buf) - - case let .cryptoPsbt(psbt): + + case .confirmed: writeInt(&buf, Int32(2)) - FfiConverterString.write(psbt, into: &buf) - - case let .cbor(urType,cbor): + + case .failed: writeInt(&buf, Int32(3)) - FfiConverterString.write(urType, into: &buf) - FfiConverterData.write(cbor, into: &buf) - + + + case .bridging: + writeInt(&buf, Int32(4)) + + + case .bridgeNeedsAttention: + writeInt(&buf, Int32(5)) + + + case .replaced: + writeInt(&buf, Int32(6)) + } } } @@ -22760,21 +24514,21 @@ public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrPayload_lift(_ buf: RustBuffer) throws -> UrPayload { - return try FfiConverterTypeUrPayload.lift(buf) +public func FfiConverterTypeUsdtTransferStatus_lift(_ buf: RustBuffer) throws -> UsdtTransferStatus { + return try FfiConverterTypeUsdtTransferStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrPayload_lower(_ value: UrPayload) -> RustBuffer { - return FfiConverterTypeUrPayload.lower(value) +public func FfiConverterTypeUsdtTransferStatus_lower(_ value: UsdtTransferStatus) -> RustBuffer { + return FfiConverterTypeUsdtTransferStatus.lower(value) } -extension UrPayload: Equatable, Hashable {} +extension UsdtTransferStatus: Equatable, Hashable {} -extension UrPayload: Codable {} +extension UsdtTransferStatus: Codable {} @@ -23905,6 +25659,30 @@ fileprivate struct FfiConverterOptionTypeTrezorFeatures: FfiConverterRustBuffer } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeUsdtDepositOrder: FfiConverterRustBuffer { + typealias SwiftType = UsdtDepositOrder? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeUsdtDepositOrder.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeUsdtDepositOrder.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -25260,6 +27038,56 @@ fileprivate struct FfiConverterSequenceTypeTxOutput: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeUsdtDeposit: FfiConverterRustBuffer { + typealias SwiftType = [UsdtDeposit] + + public static func write(_ value: [UsdtDeposit], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeUsdtDeposit.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UsdtDeposit] { + let len: Int32 = try readInt(&buf) + var seq = [UsdtDeposit]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeUsdtDeposit.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeUsdtTransfer: FfiConverterRustBuffer { + typealias SwiftType = [UsdtTransfer] + + public static func write(_ value: [UsdtTransfer], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeUsdtTransfer.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UsdtTransfer] { + let len: Int32 = try readInt(&buf) + var seq = [UsdtTransfer]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeUsdtTransfer.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -25385,6 +27213,31 @@ fileprivate struct FfiConverterSequenceTypeHardwareWalletTransport: FfiConverter } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeUsdtDepositNetwork: FfiConverterRustBuffer { + typealias SwiftType = [UsdtDepositNetwork] + + public static func write(_ value: [UsdtDepositNetwork], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeUsdtDepositNetwork.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UsdtDepositNetwork] { + let len: Int32 = try readInt(&buf) + var seq = [UsdtDepositNetwork]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeUsdtDepositNetwork.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -28041,6 +29894,35 @@ public func urEncodeCryptoPsbt(psbt: String, maxFragmentLength: UInt32)throws - ) }) } +public func usdtAddress(mnemonic: String, passphrase: String?)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_func_usdt_address( + FfiConverterString.lower(mnemonic), + FfiConverterOptionString.lower(passphrase),$0 + ) +}) +} +public func usdtFormatAmount(amount: UInt64) -> String { + return try! FfiConverterString.lift(try! rustCall() { + uniffi_bitkitcore_fn_func_usdt_format_amount( + FfiConverterUInt64.lower(amount),$0 + ) +}) +} +public func usdtParseAmount(value: String)throws -> UInt64 { + return try FfiConverterUInt64.lift(try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_func_usdt_parse_amount( + FfiConverterString.lower(value),$0 + ) +}) +} +public func usdtParsePaymentRequest(value: String)throws -> UsdtPaymentRequest { + return try FfiConverterTypeUsdtPaymentRequest_lift(try rustCallWithError(FfiConverterTypeUsdtError_lift) { + uniffi_bitkitcore_fn_func_usdt_parse_payment_request( + FfiConverterString.lower(value),$0 + ) +}) +} public func validateBitcoinAddress(address: String)throws -> ValidationResult { return try FfiConverterTypeValidationResult_lift(try rustCallWithError(FfiConverterTypeAddressError_lift) { uniffi_bitkitcore_fn_func_validate_bitcoin_address( @@ -28691,6 +30573,18 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_func_ur_encode_crypto_psbt() != 32954) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_func_usdt_address() != 48498) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_usdt_format_amount() != 13064) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_usdt_parse_amount() != 3421) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_usdt_parse_payment_request() != 63265) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_func_validate_bitcoin_address() != 56003) { return InitializationResult.apiChecksumMismatch } @@ -28775,9 +30669,54 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_method_urdecoder_reset() != 6027) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_method_usdtdepositclient_detail() != 63177) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtdepositclient_history() != 10976) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtdepositclient_networks() != 27890) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtdepositclient_receive() != 27375) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtdepositclient_request_refund() != 10045) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_balance() != 12328) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_history() != 4617) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_quote_transfer() != 3732) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_receive_address() != 540) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri() != 33484) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfers() != 32305) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_send() != 10847) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_method_usdtwallet_sync_history() != 48106) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_constructor_urdecoder_new() != 23014) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_constructor_usdtdepositclient_new() != 44626) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_constructor_usdtwallet_new() != 63633) { + return InitializationResult.apiChecksumMismatch + } uniffiCallbackInitBoltzEventListener() uniffiCallbackInitEventListener() diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index f6e06a7..24fb403 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -660,6 +660,101 @@ RustBuffer uniffi_bitkitcore_fn_method_urdecoder_receive(void*_Nonnull ptr, Rust void uniffi_bitkitcore_fn_method_urdecoder_reset(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTDEPOSITCLIENT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTDEPOSITCLIENT +void*_Nonnull uniffi_bitkitcore_fn_clone_usdtdepositclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTDEPOSITCLIENT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTDEPOSITCLIENT +void uniffi_bitkitcore_fn_free_usdtdepositclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW +void*_Nonnull uniffi_bitkitcore_fn_constructor_usdtdepositclient_new(RustBuffer address, RustBuffer service_url, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_DETAIL +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_DETAIL +uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_detail(void*_Nonnull ptr, RustBuffer deposit_id, uint32_t offset, RustBuffer mnemonic, RustBuffer passphrase +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_HISTORY +uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_history(void*_Nonnull ptr, uint32_t offset, RustBuffer mnemonic, RustBuffer passphrase +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_NETWORKS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_NETWORKS +uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_networks(void*_Nonnull ptr +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_RECEIVE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_RECEIVE +uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_receive(void*_Nonnull ptr, RustBuffer network, uint64_t amount, RustBuffer mnemonic, RustBuffer passphrase +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND +uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_request_refund(void*_Nonnull ptr, RustBuffer deposit_id, uint32_t offset, RustBuffer refund_address, RustBuffer network, RustBuffer mnemonic, RustBuffer passphrase +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTWALLET +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTWALLET +void*_Nonnull uniffi_bitkitcore_fn_clone_usdtwallet(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTWALLET +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTWALLET +void uniffi_bitkitcore_fn_free_usdtwallet(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTWALLET_NEW +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTWALLET_NEW +void*_Nonnull uniffi_bitkitcore_fn_constructor_usdtwallet_new(RustBuffer address, RustBuffer storage_path, RustBuffer rpc_url, RustBuffer bundler_url, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_BALANCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_BALANCE +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_balance(void*_Nonnull ptr +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_HISTORY +RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_history(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_QUOTE_TRANSFER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_QUOTE_TRANSFER +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_quote_transfer(void*_Nonnull ptr, RustBuffer recipient, uint64_t amount, RustBuffer destination +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_RECEIVE_ADDRESS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_RECEIVE_ADDRESS +RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_address(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_RECEIVE_URI +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_RECEIVE_URI +RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_uri(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfers(void*_Nonnull ptr +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_SEND +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_SEND +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_send(void*_Nonnull ptr, RustBuffer quote_id, RustBuffer mnemonic, RustBuffer passphrase +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_SYNC_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_SYNC_HISTORY +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_sync_history(void*_Nonnull ptr +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_ACTIVITIES_FROM_JSON #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_ACTIVITIES_FROM_JSON RustBuffer uniffi_bitkitcore_fn_func_activities_from_json(RustBuffer json, RustCallStatus *_Nonnull out_status @@ -1694,6 +1789,26 @@ void uniffi_bitkitcore_fn_func_upsert_transaction_details(RustBuffer details_lis RustBuffer uniffi_bitkitcore_fn_func_ur_encode_crypto_psbt(RustBuffer psbt, uint32_t max_fragment_length, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_ADDRESS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_ADDRESS +RustBuffer uniffi_bitkitcore_fn_func_usdt_address(RustBuffer mnemonic, RustBuffer passphrase, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_FORMAT_AMOUNT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_FORMAT_AMOUNT +RustBuffer uniffi_bitkitcore_fn_func_usdt_format_amount(uint64_t amount, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_PARSE_AMOUNT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_PARSE_AMOUNT +uint64_t uniffi_bitkitcore_fn_func_usdt_parse_amount(RustBuffer value, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_PARSE_PAYMENT_REQUEST +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_USDT_PARSE_PAYMENT_REQUEST +RustBuffer uniffi_bitkitcore_fn_func_usdt_parse_payment_request(RustBuffer value, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_VALIDATE_BITCOIN_ADDRESS #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_VALIDATE_BITCOIN_ADDRESS RustBuffer uniffi_bitkitcore_fn_func_validate_bitcoin_address(RustBuffer address, RustCallStatus *_Nonnull out_status @@ -3194,6 +3309,30 @@ uint16_t uniffi_bitkitcore_checksum_func_upsert_transaction_details(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_UR_ENCODE_CRYPTO_PSBT uint16_t uniffi_bitkitcore_checksum_func_ur_encode_crypto_psbt(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_ADDRESS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_ADDRESS +uint16_t uniffi_bitkitcore_checksum_func_usdt_address(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_FORMAT_AMOUNT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_FORMAT_AMOUNT +uint16_t uniffi_bitkitcore_checksum_func_usdt_format_amount(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_PARSE_AMOUNT +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_PARSE_AMOUNT +uint16_t uniffi_bitkitcore_checksum_func_usdt_parse_amount(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_PARSE_PAYMENT_REQUEST +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_USDT_PARSE_PAYMENT_REQUEST +uint16_t uniffi_bitkitcore_checksum_func_usdt_parse_payment_request(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_VALIDATE_BITCOIN_ADDRESS @@ -3362,12 +3501,102 @@ uint16_t uniffi_bitkitcore_checksum_method_urdecoder_receive(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_URDECODER_RESET uint16_t uniffi_bitkitcore_checksum_method_urdecoder_reset(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_DETAIL +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_DETAIL +uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_detail(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_HISTORY +uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_history(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_NETWORKS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_NETWORKS +uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_networks(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_RECEIVE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_RECEIVE +uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_receive(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND +uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_request_refund(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_BALANCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_BALANCE +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_balance(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_HISTORY +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_history(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_QUOTE_TRANSFER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_QUOTE_TRANSFER +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_quote_transfer(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_ADDRESS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_ADDRESS +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_address(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_URI +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_URI +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFERS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFERS +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfers(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_SEND +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_SEND +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_send(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_SYNC_HISTORY +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_SYNC_HISTORY +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_sync_history(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_URDECODER_NEW #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_URDECODER_NEW uint16_t uniffi_bitkitcore_checksum_constructor_urdecoder_new(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW +uint16_t uniffi_bitkitcore_checksum_constructor_usdtdepositclient_new(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTWALLET_NEW +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTWALLET_NEW +uint16_t uniffi_bitkitcore_checksum_constructor_usdtwallet_new(void + ); #endif #ifndef UNIFFI_FFIDEF_FFI_BITKITCORE_UNIFFI_CONTRACT_VERSION diff --git a/src/lib.rs b/src/lib.rs index 6aefd74..9266c44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,12 @@ pub use modules::lnurl; pub use modules::onchain; pub use modules::scanner::{DecodingError, LnurlPayData, Scanner}; pub use modules::seedqr::{decode_compact_seed_qr, decode_standard_seed_qr, SeedQrError}; +pub use modules::usdt::{ + usdt_address, usdt_format_amount, usdt_parse_amount, usdt_parse_payment_request, UsdtDeposit, + UsdtDepositAddress, UsdtDepositClient, UsdtDepositDetail, UsdtDepositNetwork, UsdtDepositOrder, + UsdtDepositPage, UsdtDestination, UsdtError, UsdtPaymentRequest, UsdtQuote, UsdtTransfer, + UsdtTransferStatus, UsdtWallet, +}; use bip39::Mnemonic; use bitcoin::bip32::Xpriv; diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 5b2ff2a..d628203 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -10,3 +10,4 @@ pub mod scanner; pub mod seedqr; pub mod trezor; pub mod ur; +pub mod usdt; diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md new file mode 100644 index 0000000..a2004dc --- /dev/null +++ b/src/modules/usdt/README.md @@ -0,0 +1,126 @@ +# USDT on Arbitrum One + +Bitkit keeps a separate USDT balance, receive address, authenticated payment flow and activity list. Rust owns account derivation, quotes, signing, persistence and chain access. The native apps retain their existing keychain, authentication, lifecycle and UI components. + +## Account and recovery + +The receive address is the Ethereum address derived from the existing Bitkit BIP39 mnemonic and passphrase at `m/44'/60'/0'/0/0`. EIP-7702 delegates that address to the pinned Simple7702Account implementation (`0xe6Cae83BdE06E4c305530e199D7217f42808555B`) using EntryPoint 0.8 (`0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108`). + +Each authenticated payment signs a chain-specific authorization for the pinned delegate at the current account nonce. This keeps the operation hash bound to that delegate and satisfies the bundler’s `0x7702` factory-marker policy, including after activation. Authorization gas is included in the quoted USDT maximum. Quotes use dummy authorization; they never receive a signing key. There is no factory deployment or separate smart-wallet address. + +Restoring the same seed and passphrase reconstructs the same address and reads its balance and activity from Arbitrum. Another wallet using the same derivation can access that address, although its gas-payment and delegation support may differ. + +Delegation persists onchain and can be applied even if the operation fails. Retries retain both signatures and require the authorization nonce to remain current. If authorization was consumed without a known payment result, the signed payment stays pending until an event identifies its outcome or its paymaster terms expire with the EntryPoint nonce unchanged. A different delegate blocks Bitkit sends; balances and incoming/outgoing token history remain readable. Bitkit does not silently replace another wallet's delegation. + +The native keychain is the source of signing credentials. Rust zeroizes its owned mnemonic/passphrase/seed buffers and best-effort erases signing keys; FFI and library-internal copies cannot be guaranteed erased; SQLite stores no seed or private key. + +Signed operations are stored before transmission. A lost response only retries the identical operation, and a quote ID cannot authorize a second payment. + +Expiry of the signed paymaster terms with an unchanged on-chain nonce resolves an unmined submission. The shorter local quote deadline cannot release a signed payment. Network timestamps govern these checks. A higher nonce remains pending until an event identifies this payment or its replacement. + +New attempts require another quote and native authorization. One source-chain payment can be pending at a time; confirmed bridge messages can continue delivery while another payment is made. + +`balance()` returns USDT in millionths. `sync_history()` returns `true` when caught up and `false` when its 20-second work budget expires; request failures and invalid data return errors. Callers retain recovered activity and continue incomplete scans on a later refresh. Local activity has no fixed entry cap. + +History scans bounded ranges from genesis, including deposits before delegation. Each completed page and partial-page receipt survives interruption; a completed scan revisits 4096 blocks for delayed indexing. Scans trail the reported tip by two blocks; this lag and revisit do not provide reorg rollback or retract previously saved orphaned activity. + +Incoming and outgoing transfers decode from token logs, including transfers made with another wallet. A combined incoming/EntryPoint filter and an outgoing filter require two log queries per range. Known timestamps are reused. + +Locally recorded payments are matched by their operation hash. For other payments, supported account calls recover payment and fee details only from transactions sent directly to the pinned EntryPoint. Their raw outgoing logs are excluded to avoid duplicates. Unknown wrappers and call shapes retain their token transfers without inventing payment or fee attribution. + +Range and response-size limits reduce the query span; rate limits return a distinct error. Pending-payment recovery can fall back to historical nonce reads and a single-block event query when its full log range is rejected. The RPC provider must support those historical state reads as well as full-history filtered logs. + +## Fees and infrastructure + +Only the exact Arbitrum USDT0 token is supported: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`, with six decimals. Amounts and fees are integers; no floating point or ETH balance is exposed to the user. + +Pimlico's ERC20 paymaster is the initial provider. Its official supported-token list includes this token. The pinned EntryPoint 0.8 paymaster is `0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402`. + +A payment atomically batches a limited token approval and execution at the delegated address. Gas, token conversion, fixed charges and expiry are checked before the user approves the maximum fee. Final provider data, calldata and limits are signed together using the EntryPoint 0.8 EIP-712 hash, including the delegate. + +Local quotes last at most 120 seconds; newly quoted paymaster terms must have a finite deadline within 15 minutes. Recovery of a signed operation always follows its actual signed deadline. + +The approval has a 5% margin, while the displayed maximum fee uses the signed gas limits and paymaster terms, not the allowance. Call gas and pre-verification gas receive a 10% margin for execution and Arbitrum L1 data-cost variation; provider verification estimates are retained. The pre-verification margin is charged as part of the payment fee and included in the displayed maximum. Changed or expired terms require another review. + +A residual allowance to the pinned paymaster can remain after collection; each subsequent payment sets a new finite allowance. The helper allowance is revoked in the bridge batch. + +Mainnet paymaster access requires a Pimlico project. Keep provider credentials on the server and validate the configured provider's pricing and policy before release. + +Configure `USDT_BUNDLER_URL` with a controlled HTTPS endpoint forwarding the provider's bundler/paymaster JSON-RPC methods. Do not distribute a private provider key in the apps. A production proxy must retain the key, restrict chain/token/methods and apply abuse controls. Its provisioning belongs to the backend deployment, not to the mobile keychain. + +The apps intentionally show USDT as unavailable when this endpoint is missing. Both endpoints are required by the core constructor; there is no implicit public RPC. + +`USDT_RPC_URL` must point to the service's credential-free `/v1/usdt/chain-rpc` endpoint in both mobile apps. The private upstream URL is configured on the service as `ARBITRUM_RPC_URL`; it must never enter a mobile build. Rust allows plain HTTP only on localhost for fixtures. + +Chain and bundler calls share a budget of 80 requests per minute with an initial burst of 20. Native wallets poll every 30 seconds while idle and every ten seconds while receiving or awaiting a pending payment, with a shared ten-second minimum and one-minute backoff after rate limits. History scanning continues after a settlement error, but cancellation and throttling stop the refresh. + +The standalone `bitkit-usdt-service` implements this endpoint at `/v1/usdt/rpc`. It runs independently of Blocktank, validates the pinned EIP-7702/USDT request shapes, and applies per-IP/global request and concurrency limits. Configure the apps with its full HTTPS route. + +It adds no wallet login or custody; the endpoint is public with constrained operations, not proof of official-app identity. Its README documents provider credentials, reverse-proxy configuration, limits and local testing. Provisioning and live-provider acceptance remain deployment prerequisites. + +- iOS reads these names from build settings/Info.plist or process configuration for local testing. +- Android reads these names through its existing local-properties/environment build configuration. +- Arbitrum One (42161) is the only supported USDT chain, including development builds. The UI always labels it. Bitcoin's network selection does not imply USDT testnet support. Storage remains under the existing Bitcoin-network/wallet directory, with a USDT-specific database and identity check. + +## Deposits and withdrawals + +Receive shares an ERC-681 token-transfer request identifying Arbitrum One and USDT0, and allows copying the raw account address. The sender must select Arbitrum One or bridge into it. + +`usdt_parse_payment_request()` accepts the pinned token-transfer URI with an optional `uint256` amount, including exact scientific notation in atomic units. Native send screens prefill that amount for editing and review. Duplicate or unsupported parameters are rejected. `quote_transfer()` takes a raw recipient address and an explicit amount after payment-request parsing and user review; it rejects URIs rather than discarding their requested amounts. + +Plain transfers to that address on Ethereum, TRON or another chain are not automatically forwarded. + +The optional Orchestra receive flow instead provides a separate reusable deposit address for each enabled source network. Ethereum and TRON USDT are supported by the adapter; the backend advertises only explicitly enabled, provider-supported routes into the pinned Arbitrum USDT0 account. The sender pays the source network fee, while routing costs are deducted from the deposited USDT. There is no Rhino or Relay integration. + +Configure `USDT_DEPOSITS_URL` with the service’s HTTPS `/v1/usdt/deposits` route and keep `ORCHESTRA_API_KEY` on the server. Wallet-signed requests bind deposit registration, status and explicit refund requests to the seed-derived address. Estimates are indicative; held deposits and refunds remain visible without implying settlement. + +A partner account, route acceptance, funded delivery and refund validation are release prerequisites. Without the endpoint, direct Arbitrum receive remains available. + +The native release exposes Arbitrum only. Bridge routes stay disabled at the service until destination delivery has passed funded acceptance. Same-chain payments transfer USDT directly. + +Cross-chain payments use the deployed USDT0 TransactionValueHelper, `0xa90f03c856D01F698E7071B393387cd75a8a319A`, and pinned Arbitrum OFT, `0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92`. The helper supplies native messaging value and collects USDT. Calls carry zero ETH from the account. The bridge approval covers principal plus a bounded token fee and is revoked atomically after sending. + +OFT amount/peer checks and helper balance/maxGas checks reject unavailable routes. The review screen shows the exact expected receipt and maximum additional fees. + +Supported destinations are Arbitrum One, Ethereum (30101), Polygon (30109), Plasma (30383), and Stable (30396). Arbitrum's LayerZero ID is 30110. TRON, Solana, TON and other deployments are not inferred from USDT0 branding; they are not offered. + +The helper is an operational dependency: its operator must maintain native liquidity and its oracle/markup affect pricing. Its verified deployed runtime (`0x9d4c3b4b79a3d31843ec75a03ce6ef43735421fdee489714519afc052f60ef27`) retains behaviors discussed in the OpenZeppelin audit; it is not the audit-remediated implementation. + +Bitkit constrains calls to zero native value, a pinned OFT, exact amounts, and finite approvals. Failed execution can still incur the authorized paymaster gas fee. + +Bridge-status polling shares a five-second refresh budget and rotates the first unresolved bridge checked, leaving source-chain recovery and sends available when delivery status is unavailable. + +A successful source receipt means the bridge has started. Bitkit matches the operation's GUID and LayerZero pathway before marking delivery confirmed. Failed, blocked or `PAYLOAD_STORED` delivery remains visible as requiring attention, with a tracking link. It does not silently retry a destination transaction or charge an additional recovery fee. + +The RPC sees queried wallet addresses, and LayerZero Scan sees bridge transaction hashes. Neither service holds the signing key. + +## Bindings and local builds + +Build core with the repository's `build_ios.sh` and `build_android.sh`, sequentially: the Android script temporarily changes the manifest and example source. Generated Swift/Kotlin bindings and native binaries must come from the same core source. + +For local iOS builds, `scripts/build-usdt-local.sh` in bitkit-ios takes the core directory followed by normal xcodebuild arguments and sets the `BITKIT_CORE_LOCAL` package override. Android accepts the absolute generated release AAR through the `bitkitCoreAar` Gradle property. + +## Validation + +The focused Rust suite covers independent address/signature parity, exact decimal amounts, chain-qualified QR requests, bounded paymaster and bridge fees, own-operation receipt attribution, uncertain submission, nonce/expiry recovery, and seed-restored history. The reference vector uses the public test mnemonic and ethers 6.17.0; it contains no production credential. + +The ignored deployed-contract test uses a fresh local Anvil fork of Arbitrum One on port 18545. Install the pinned test-only dependency in `tests/usdt-fork` and start its `provider.mjs` on port 18546, then run the ignored `deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically` Rust test. + +The fixture first requires an Anvil client, sets only local token balances and a local paymaster test signer, and uses the deployed contract bytecode. It exercises EIP-7702 activation, USDT post-operation collection, helper send/revoke, and helper failure. + +Its fixed estimates and test signatures do not validate Pimlico's real API policy, pricing or production credentials. Restart both fixture processes with a fresh fork for another run. + +To include the provider service in that same test, start it with `NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then set `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` when running the ignored test. These overrides affect only the fork test; normal wallet configuration uses `USDT_RPC_URL` and `USDT_BUNDLER_URL`. + +## References + +- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) +- [Alto EIP-7702 request validation](https://github.com/pimlicolabs/alto/blob/96529592b67a69be23c013359cbc9990657af64a/src/rpc/rpcHandler.ts) +- [Simple7702Account](https://github.com/eth-infinitism/account-abstraction/blob/releases/v0.8/contracts/accounts/Simple7702Account.sol) +- [Pimlico supported tokens](https://docs.pimlico.io/references/paymaster/erc20-paymaster/supported-tokens) +- [Pimlico pricing](https://www.pimlico.io/pricing) +- [Pimlico public endpoint limits](https://docs.pimlico.io/references/bundler/public-endpoint) +- [USDT0 documentation](https://docs.usdt0.to/) +- [Transaction helper audit](https://www.openzeppelin.com/news/usdt0-transaction-helper-audit) +- [Verified deployed helper](https://arbitrum.blockscout.com/api/v2/smart-contracts/0xa90f03c856d01f698e7071b393387cd75a8a319a) +- [LayerZero message statuses](https://docs.layerzero.network/v2/tools/layerzeroscan/mainnet/messages/get-messagesstatus) diff --git a/src/modules/usdt/account.rs b/src/modules/usdt/account.rs new file mode 100644 index 0000000..5b966cc --- /dev/null +++ b/src/modules/usdt/account.rs @@ -0,0 +1,38 @@ +use super::UsdtError; +use alloy_primitives::{address, Address, Bytes, U256}; +use alloy_sol_types::{sol, SolCall}; + +pub(super) const ENTRY_POINT: Address = address!("4337084D9E255Ff0702461CF8895CE9E3b5Ff108"); +pub(super) const DELEGATE: Address = address!("e6Cae83BdE06E4c305530e199D7217f42808555B"); + +sol! { + struct Call { address target; uint256 value; bytes data; } + interface SimpleAccount { + function execute(address target, uint256 value, bytes data); + function executeBatch(Call[] calls); + } +} + +pub(super) fn validate_delegation(code: &[u8]) -> Result<(), UsdtError> { + if code.is_empty() + || (code.len() == 23 && code[..3] == [0xef, 0x01, 0x00] && code[3..] == DELEGATE[..]) + { + return Ok(()); + } + Err(UsdtError::UnsupportedDelegation) +} + +pub(super) fn batch(calls: &[(Address, Bytes)]) -> Bytes { + SimpleAccount::executeBatchCall { + calls: calls + .iter() + .map(|(target, data)| Call { + target: *target, + value: U256::ZERO, + data: data.clone(), + }) + .collect(), + } + .abi_encode() + .into() +} diff --git a/src/modules/usdt/amount.rs b/src/modules/usdt/amount.rs new file mode 100644 index 0000000..a99cc1c --- /dev/null +++ b/src/modules/usdt/amount.rs @@ -0,0 +1,75 @@ +use super::errors::UsdtError; +use alloy_primitives::U256; + +#[uniffi::export] +pub fn usdt_parse_amount(value: String) -> Result { + let mut parts = value.trim().split('.'); + let whole = parts.next().ok_or(UsdtError::InvalidAmount)?; + let fraction = parts.next().unwrap_or(""); + if parts.next().is_some() + || whole.is_empty() + || !whole.bytes().all(|b| b.is_ascii_digit()) + || fraction.len() > 6 + || !fraction.bytes().all(|b| b.is_ascii_digit()) + { + return Err(UsdtError::InvalidAmount); + } + let whole: u64 = whole.parse().map_err(|_| UsdtError::InvalidAmount)?; + let fraction: u64 = format!("{fraction:0<6}") + .parse() + .map_err(|_| UsdtError::InvalidAmount)?; + whole + .checked_mul(1_000_000) + .and_then(|v| v.checked_add(fraction)) + .ok_or(UsdtError::InvalidAmount) +} + +#[uniffi::export] +pub fn usdt_format_amount(amount: u64) -> String { + let value = format!("{}.{:06}", amount / 1_000_000, amount % 1_000_000); + value + .trim_end_matches('0') + .trim_end_matches('.') + .to_string() +} + +pub(super) fn token_amount(value: U256) -> Result { + value.try_into().map_err(|_| UsdtError::InvalidAmount) +} + +// ERC-681 token amounts are integers in atomic units, optionally in scientific notation. +pub(super) fn parse_atomic_amount(value: &str) -> Result { + let (mantissa, exponent) = match value.split_once(['e', 'E']) { + Some((mantissa, exponent)) => ( + mantissa, + exponent + .parse::() + .map_err(|_| UsdtError::InvalidAmount)?, + ), + None => (value, 0), + }; + let mantissa = mantissa.strip_prefix('+').unwrap_or(mantissa); + let (whole, fraction) = mantissa.split_once('.').unwrap_or((mantissa, "")); + if whole.is_empty() && fraction.is_empty() + || !whole + .bytes() + .chain(fraction.bytes()) + .all(|c| c.is_ascii_digit()) + { + return Err(UsdtError::InvalidAmount); + } + let fraction = fraction.trim_end_matches('0'); + let power = exponent + .checked_sub(fraction.len() as u32) + .ok_or(UsdtError::InvalidAmount)?; + let digits = format!("0{whole}{fraction}"); + let amount = digits + .parse::() + .map_err(|_| UsdtError::InvalidAmount)?; + if amount == 0 { + return Ok(0); + } + amount + .checked_mul(10u64.checked_pow(power).ok_or(UsdtError::InvalidAmount)?) + .ok_or(UsdtError::InvalidAmount) +} diff --git a/src/modules/usdt/deposits.rs b/src/modules/usdt/deposits.rs new file mode 100644 index 0000000..1ceea71 --- /dev/null +++ b/src/modules/usdt/deposits.rs @@ -0,0 +1,383 @@ +use super::{ + keys::{derive_key, key_address, parse_address}, + UsdtError, +}; +use alloy_primitives::{keccak256, Address}; +use bitcoin::secp256k1::{Message, Secp256k1}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; +use serde_json::{json, Value}; +use std::{sync::Arc, time::Duration}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] +#[serde(rename_all = "lowercase")] +pub enum UsdtDepositNetwork { + Ethereum, + Tron, +} + +#[derive(Clone, Debug, Deserialize, uniffi::Record)] +pub struct UsdtDepositAddress { + pub network: UsdtDepositNetwork, + pub address: String, + pub recipient: String, + #[serde(deserialize_with = "number")] + pub amount: u64, + #[serde(deserialize_with = "number")] + pub estimated_received: u64, + pub min_usd_cents: Option, + pub max_usd_cents: Option, + pub slippage_bps: u32, + #[serde(default)] + pub uri: String, +} + +#[derive(Clone, Debug, Deserialize, uniffi::Record)] +pub struct UsdtDeposit { + pub id: String, + pub network: String, + pub asset: String, + #[serde(deserialize_with = "optional_number")] + pub amount: Option, + pub source_tx: String, + pub status: String, + pub code: Option, + pub refund_tx: Option, +} + +#[derive(Clone, Debug, Deserialize, uniffi::Record)] +pub struct UsdtDepositPage { + pub deposits: Vec, + pub next_offset: Option, +} + +#[derive(Clone, Debug, Deserialize, uniffi::Record)] +pub struct UsdtDepositOrder { + pub status: String, + #[serde(deserialize_with = "optional_number")] + pub amount_in: Option, + #[serde(deserialize_with = "optional_number")] + pub amount_out: Option, + pub destination_tx: Option, + pub refund_tx: Option, + pub code: Option, +} + +#[derive(Clone, Debug, Deserialize, uniffi::Record)] +pub struct UsdtDepositDetail { + pub deposit: UsdtDeposit, + pub order: Option, +} + +#[derive(uniffi::Object)] +pub struct UsdtDepositClient { + address: Address, + client: reqwest::Client, + url: String, +} + +#[uniffi::export(async_runtime = "tokio")] +impl UsdtDepositClient { + #[uniffi::constructor] + pub fn new(address: String, service_url: String) -> Result, UsdtError> { + let url = url::Url::parse(&service_url).map_err(|_| UsdtError::NotConfigured)?; + if (!matches!(url.scheme(), "https") + && !(url.scheme() == "http" + && matches!(url.host_str(), Some("localhost" | "127.0.0.1")))) + || !url.username().is_empty() + || url.password().is_some() + || url.query().is_some() + || url.fragment().is_some() + { + return Err(UsdtError::NotConfigured); + } + Ok(Arc::new(Self { + address: parse_address(&address)?, + client: reqwest::Client::builder() + .timeout(Duration::from_secs(22)) + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(|_| UsdtError::NetworkUnavailable)?, + url: service_url, + })) + } + + pub async fn networks(&self) -> Result, UsdtError> { + #[derive(Deserialize)] + struct Networks { + networks: Vec, + } + Ok(self + .response::(self.client.get(&self.url)) + .await? + .networks) + } + + pub async fn receive( + &self, + network: UsdtDepositNetwork, + amount: u64, + mnemonic: String, + passphrase: Option, + ) -> Result { + if amount == 0 { + return Err(UsdtError::InvalidAmount); + } + let mut result: UsdtDepositAddress = self + .call( + json!({"action":"receive", "network":network, + "amount":amount.to_string()}), + mnemonic, + passphrase, + ) + .await?; + if result.network != network + || parse_address(&result.recipient)? != self.address + || result.amount != amount + || result.estimated_received == 0 + || result.slippage_bps != 50 + { + return Err(UsdtError::InvalidResponse); + } + validate_source_address(&result.address, network)?; + result.uri = match network { + UsdtDepositNetwork::Ethereum => format!( + "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7@1/transfer?address={}", + result.address + ), + UsdtDepositNetwork::Tron => result.address.clone(), + }; + Ok(result) + } + + pub async fn history( + &self, + offset: u32, + mnemonic: String, + passphrase: Option, + ) -> Result { + self.call( + json!({"action":"history","offset":offset}), + mnemonic, + passphrase, + ) + .await + } + + pub async fn detail( + &self, + deposit_id: String, + offset: u32, + mnemonic: String, + passphrase: Option, + ) -> Result { + let result: UsdtDepositDetail = self + .call( + json!({"action":"detail","depositId":deposit_id,"offset":offset}), + mnemonic, + passphrase, + ) + .await?; + if result.deposit.id != deposit_id { + return Err(UsdtError::InvalidResponse); + } + Ok(result) + } + + pub async fn request_refund( + &self, + deposit_id: String, + offset: u32, + refund_address: String, + network: UsdtDepositNetwork, + mnemonic: String, + passphrase: Option, + ) -> Result<(), UsdtError> { + validate_source_address(refund_address.trim(), network)?; + let result: Value = self + .call( + json!({"action":"refund","depositId":deposit_id,"offset":offset, + "refundAddress":refund_address.trim()}), + mnemonic, + passphrase, + ) + .await?; + if result["status"] != "refund_requested" { + return Err(UsdtError::InvalidResponse); + } + Ok(()) + } +} + +impl UsdtDepositClient { + async fn call( + &self, + payload: Value, + mnemonic: String, + passphrase: Option, + ) -> Result { + let signed = self.authorize(payload, mnemonic, passphrase, super::wallet::now())?; + self.response(self.client.post(&self.url).json(&signed)) + .await + } + + fn authorize( + &self, + payload: Value, + mnemonic: String, + passphrase: Option, + timestamp: u64, + ) -> Result { + let key = derive_key(mnemonic, passphrase)?; + if key_address(&key) != self.address { + return Err(UsdtError::InvalidCredentials); + } + let request = + json!({"owner":self.address.to_checksum(None),"timestamp":timestamp,"payload":payload}) + .to_string(); + let message = format!("Bitkit USDT deposits v1\n{request}"); + let hash = keccak256(format!( + "\x19Ethereum Signed Message:\n{}{message}", + message.len() + )); + let (recovery, signature) = Secp256k1::new() + .sign_ecdsa_recoverable(&Message::from_digest(hash.0), &key) + .serialize_compact(); + let signature = format!("0x{}{:02x}", hex::encode(signature), recovery.to_i32() + 27); + drop(key); + Ok(json!({"request":request,"signature":signature})) + } + + async fn response( + &self, + request: reqwest::RequestBuilder, + ) -> Result { + let mut response = request + .send() + .await + .map_err(|_| UsdtError::NetworkUnavailable)?; + let status = response.status(); + if status == reqwest::StatusCode::TOO_MANY_REQUESTS { + return Err(UsdtError::RateLimited); + } + let mut bytes = Vec::new(); + while let Some(chunk) = response + .chunk() + .await + .map_err(|_| UsdtError::NetworkUnavailable)? + { + if bytes.len() + chunk.len() > 262144 { + return Err(UsdtError::InvalidResponse); + } + bytes.extend_from_slice(&chunk); + } + if !status.is_success() { + let error: Value = serde_json::from_slice(&bytes).unwrap_or_default(); + return Err(match error["error"].as_str() { + Some("not_configured") => UsdtError::NotConfigured, + Some("invalid_authorization") => UsdtError::InvalidCredentials, + Some("amount_too_small" | "amount_too_large" | "amount_exceeds_liquidity") => { + UsdtError::InvalidAmount + } + Some("invalid_refund_address") => UsdtError::InvalidAddress, + Some("route_unavailable") => UsdtError::UnsupportedRoute, + Some( + "refund_not_available" + | "instruction_conflict" + | "not_found" + | "operator_required" + | "standing_tron_refund_requires_operator", + ) => UsdtError::DepositNeedsAttention, + _ => UsdtError::NetworkUnavailable, + }); + } + serde_json::from_slice(&bytes).map_err(Into::into) + } +} + +fn validate_source_address(value: &str, network: UsdtDepositNetwork) -> Result<(), UsdtError> { + match network { + UsdtDepositNetwork::Ethereum => { + parse_address(value)?; + } + UsdtDepositNetwork::Tron => { + let payload = + bitcoin::base58::decode_check(value).map_err(|_| UsdtError::InvalidAddress)?; + if payload.len() != 21 || payload[0] != 0x41 { + return Err(UsdtError::InvalidAddress); + } + } + } + Ok(()) +} + +fn number<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + String::deserialize(deserializer)? + .parse() + .map_err(serde::de::Error::custom) +} +fn optional_number<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .map(|v| v.parse().map_err(serde::de::Error::custom)) + .transpose() +} + +#[cfg(test)] +mod tests { + use super::*; + const PHRASE: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + + #[test] + fn deposit_authorization_matches_ethers_and_rejects_another_wallet() { + let vector: Value = + serde_json::from_str(include_str!("fixtures/deposit-signature.json")).unwrap(); + let request: Value = serde_json::from_str(vector["request"].as_str().unwrap()).unwrap(); + let client = UsdtDepositClient::new( + request["owner"].as_str().unwrap().into(), + "https://example.com/v1/usdt/deposits".into(), + ) + .unwrap(); + assert_eq!( + client + .authorize( + request["payload"].clone(), + PHRASE.into(), + None, + 1_800_000_000 + ) + .unwrap(), + vector + ); + assert!(matches!( + client.authorize( + request["payload"].clone(), + PHRASE.into(), + Some("other".into()), + 1_800_000_000 + ), + Err(UsdtError::InvalidCredentials) + )); + } + + #[test] + fn deposit_addresses_and_transport_reject_wrong_networks() { + let tron = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; + assert!(validate_source_address(tron, UsdtDepositNetwork::Tron).is_ok()); + assert!(validate_source_address(tron, UsdtDepositNetwork::Ethereum).is_err()); + assert!(validate_source_address( + "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6s", + UsdtDepositNetwork::Tron + ) + .is_err()); + for url in [ + "http://example.com", + "https://user:password@example.com", + "https://example.com?key=secret", + ] { + assert!(UsdtDepositClient::new( + "0x1111111111111111111111111111111111111111".into(), + url.into() + ) + .is_err()); + } + } +} diff --git a/src/modules/usdt/errors.rs b/src/modules/usdt/errors.rs new file mode 100644 index 0000000..2d9def1 --- /dev/null +++ b/src/modules/usdt/errors.rs @@ -0,0 +1,53 @@ +use thiserror::Error; + +#[derive(Debug, Error, uniffi::Error)] +pub enum UsdtError { + #[error("Enter a valid USDT amount with at most six decimal places")] + InvalidAmount, + #[error("Enter a valid address for the selected network")] + InvalidAddress, + #[error("The payment request is for a different network or token")] + WrongNetwork, + #[error("Wallet credentials do not match this USDT account")] + InvalidCredentials, + #[error("This account uses another wallet's smart account. Restore its delegation before sending with Bitkit")] + UnsupportedDelegation, + #[error("The USDT balance does not cover the amount and maximum fee")] + InsufficientBalance, + #[error("The fee quote has expired or changed. Review a new quote")] + QuoteExpired, + #[error("A USDT transaction is pending. Wait for confirmation before sending again")] + PendingTransfer, + #[error("The selected USDT0 route is unavailable")] + UnsupportedRoute, + #[error("This deposit needs provider assistance. Check its recovery status")] + DepositNeedsAttention, + #[error("USDT payments are not configured for this app build")] + NotConfigured, + #[error("The network could not be reached. Try again")] + NetworkUnavailable, + #[error("Too many requests. Try again shortly")] + RateLimited, + #[error("The requested log range exceeds the provider limit")] + LogRangeTooLarge, + #[error("The network rejected the transaction: {reason}")] + TransactionRejected { reason: String }, + #[error("USDT wallet storage failed: {reason}")] + Storage { reason: String }, + #[error("Invalid response from the USDT network")] + InvalidResponse, +} + +impl From for UsdtError { + fn from(error: rusqlite::Error) -> Self { + Self::Storage { + reason: error.to_string(), + } + } +} + +impl From for UsdtError { + fn from(_: serde_json::Error) -> Self { + Self::InvalidResponse + } +} diff --git a/src/modules/usdt/fixtures/deposit-signature.json b/src/modules/usdt/fixtures/deposit-signature.json new file mode 100644 index 0000000..66fa60b --- /dev/null +++ b/src/modules/usdt/fixtures/deposit-signature.json @@ -0,0 +1,4 @@ +{ + "request": "{\"owner\":\"0x9858EfFD232B4033E47d90003D41EC34EcaEda94\",\"payload\":{\"action\":\"receive\",\"amount\":\"100000000\",\"network\":\"ethereum\"},\"timestamp\":1800000000}", + "signature": "0xe4a2d8340e92339d4f0627a6bf6a9bdaaae55498726ed75155a1d891761cd8cc23d6555526f69ce111c584dad43fbd974669dd9778f69c75c1b869065e05a0381b" +} diff --git a/src/modules/usdt/fixtures/eip7702-vectors.json b/src/modules/usdt/fixtures/eip7702-vectors.json new file mode 100644 index 0000000..73ec6b7 --- /dev/null +++ b/src/modules/usdt/fixtures/eip7702-vectors.json @@ -0,0 +1,42 @@ +{ + "source": "ethers@6.17.0; EntryPoint v0.8 / Simple7702Account; generated by tests/usdt-fork/vectors.mjs", + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "calls": [ + { + "to": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "data": "0x095ea7b3000000000000000000000000888888888888ec68a58ab8094cc1ad20ba3d240200000000000000000000000000000000000000000000000000000000000186a0" + }, + { + "to": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", + "data": "0xa9059cbb000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000f4240" + } + ], + "operation": { + "sender": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "nonce": "0x0", + "factory": "0x7702", + "factoryData": "0x", + "callData": "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044095ea7b3000000000000000000000000888888888888ec68a58ab8094cc1ad20ba3d240200000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000", + "callGasLimit": "0x249f0", + "verificationGasLimit": "0x3d090", + "preVerificationGas": "0x13880", + "maxFeePerGas": "0x2faf080", + "maxPriorityFeePerGas": "0x186a0", + "paymaster": "0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402", + "paymasterVerificationGasLimit": "0x186a0", + "paymasterPostOpGasLimit": "0xc350", + "paymasterData": "0x030000006b49d200000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb90000000000000000000000000000c35000000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000186a0f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "signature": "0x", + "eip7702Auth": { + "chainId": "0xa4b1", + "address": "0xe6Cae83BdE06E4c305530e199D7217f42808555B", + "nonce": "0x0", + "yParity": "0x01", + "r": "0xfeefad4175dd3eef21df8de147d204cd7e81eac16be98715e73b5c3472f33b1d", + "s": "0x5820588fa9c00582108e4937004de97dfbc51c448bf88c5eea98d2fe70ce1b5b" + } + }, + "userOperationHash": "0x93469ab8218846c22dfd239fc50be4e1295768cbfc9d25e535205be945368998", + "signature": "0x1bd9bf42152174a707ec01b5ff131274e5f382ae347dcdc08fea3829e5aa820f78d821ae3bc33e54594b92052e07173515664048bb1738e477dd1a68626ecfb81b", + "authorizationHash": "0xa66cea9a587fc7d54a769b64061e4faa03d2dc62f3b9a05cfa65a52c170505fa" +} diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs new file mode 100644 index 0000000..2bb7e2a --- /dev/null +++ b/src/modules/usdt/history.rs @@ -0,0 +1,313 @@ +use super::{ + account::{SimpleAccount, ENTRY_POINT}, + amount::token_amount, + transaction::{event_data, BridgeHelper, EntryPoint, Erc20}, + types::{BRIDGE_HELPER, EXPLORER, OFT, TOKEN}, + UsdtDestination, UsdtError, UsdtTransfer, UsdtTransferStatus, UsdtWallet, +}; +use alloy_primitives::{Address, Bytes, U256}; +use alloy_sol_types::{SolCall, SolEvent}; +use serde_json::{json, Value}; +use std::{collections::BTreeMap, sync::atomic::Ordering}; + +impl UsdtWallet { + pub(super) async fn scan_history(&self) -> Result<(), UsdtError> { + let tip = self.block_number().await?.saturating_sub(2); + let previous = self.store.synced_block()?; + let start = self.store.history_progress()?.unwrap_or_else(|| { + previous + .map(|newest| newest.saturating_sub(4096)) + .unwrap_or(0) + }); + if start > tip { + return Err(UsdtError::NetworkUnavailable); + } + let mut next = start; + let mut maximum_width = self.history_range_limit.load(Ordering::Relaxed); + let mut width = maximum_width.min(tip - start + 1); + while next <= tip { + let end = next.saturating_add(width - 1).min(tip); + match self.history_logs(next, end).await { + Ok(transactions) => { + // Persist each receipt before advancing the range. An interrupted dense block + // resumes without re-fetching the receipts already saved in this scan. + let mut timestamps = BTreeMap::new(); + for ((block, hash), logs) in transactions { + if self.store.has_history_receipt(&hash)? { + continue; + } + let needs_receipt = logs.iter().any(|log| { + serde_json::from_value::
(log["address"].clone()) + .is_ok_and(|address| address == ENTRY_POINT) + || event_data(log) + .ok() + .and_then(|data| Erc20::Transfer::decode_log_data(&data).ok()) + .is_some_and(|event| event.from == self.address) + }); + let receipt = if needs_receipt { + self.rpc + .call("eth_getTransactionReceipt", json!([hash])) + .await? + } else { + json!({"logs": logs}) + }; + let timestamp = + if let Some(timestamp) = self.store.transaction_timestamp(&hash)? { + timestamp + } else if let Some(timestamp) = timestamps.get(&block) { + *timestamp + } else { + let timestamp = self.block_timestamp(block).await?; + timestamps.insert(block, timestamp); + timestamp + }; + let transfers = self.receipt_history(&hash, timestamp, &receipt).await?; + self.store.save_history_receipt(&transfers, &hash)?; + } + if end == tip { + self.store.complete_history(tip)?; + break; + } + next = end + 1; + self.store.save_history_progress(next)?; + width = (width * 2).min(maximum_width).min(tip - next + 1); + } + Err(UsdtError::LogRangeTooLarge) if next < end => { + width = (width / 2).max(1); + maximum_width = width; + self.history_range_limit.store(width, Ordering::Relaxed); + } + Err(error) => return Err(error), + } + } + Ok(()) + } + + async fn history_logs( + &self, + start: u64, + end: u64, + ) -> Result>, UsdtError> { + let address = self.address.into_word(); + let filter = |topics: Value| json!({"address":TOKEN,"fromBlock":U256::from(start),"toBlock":U256::from(end),"topics":topics}); + let (incoming_and_operations, outgoing): (Vec, Vec) = tokio::try_join!( + self.rpc.call("eth_getLogs", json!([{ + "address":[TOKEN, ENTRY_POINT],"fromBlock":U256::from(start),"toBlock":U256::from(end), + "topics":[[Erc20::Transfer::SIGNATURE_HASH, EntryPoint::UserOperationEvent::SIGNATURE_HASH],null,address] + }])), + self.rpc.call("eth_getLogs", json!([filter(json!([Erc20::Transfer::SIGNATURE_HASH, address]))])) + )?; + let mut transactions: BTreeMap<(u64, String), Vec> = BTreeMap::new(); + for log in incoming_and_operations.into_iter().chain(outgoing) { + if log["removed"].as_bool() == Some(true) { + continue; + } + let hash: String = serde_json::from_value(log["transactionHash"].clone())?; + let block = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) + .map_err(|_| UsdtError::InvalidResponse)?; + if !(start..=end).contains(&block) { + return Err(UsdtError::InvalidResponse); + } + transactions.entry((block, hash)).or_default().push(log); + } + Ok(transactions) + } + + async fn receipt_history( + &self, + hash: &str, + timestamp: u64, + receipt: &Value, + ) -> Result, UsdtError> { + let logs = receipt["logs"] + .as_array() + .ok_or(UsdtError::InvalidResponse)?; + let mut result = Vec::new(); + let mut owned_operations = Vec::new(); + for log in logs { + let address: Address = serde_json::from_value(log["address"].clone())?; + if address == ENTRY_POINT { + if let Ok(event) = + EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) + { + if event.sender == self.address { + let saved = self + .store + .transfer_by_hash(&format!("{:#x}", event.userOpHash))?; + owned_operations.push((event, saved)); + } + } + } + if address != TOKEN { + continue; + } + let Ok(event) = Erc20::Transfer::decode_log_data(&event_data(log)?) else { + continue; + }; + let incoming = event.to == self.address && event.from != self.address; + let outgoing = event.from == self.address && event.to != self.address; + if (!incoming && !outgoing) || event.value.is_zero() { + continue; + } + let index: U256 = serde_json::from_value(log["logIndex"].clone())?; + result.push(UsdtTransfer { + id: format!("{hash}:{index}"), + tx_hash: hash.into(), + user_operation_hash: None, + bridge_guid: None, + recipient: event.to.to_checksum(None), + destination: UsdtDestination::Arbitrum, + amount: token_amount(event.value)?, + received_amount: token_amount(event.value)?, + bridge_fee: 0, + fee: None, + is_incoming: incoming, + status: UsdtTransferStatus::Confirmed, + timestamp, + explorer_url: format!("{EXPLORER}/tx/{hash}"), + }); + } + if owned_operations.is_empty() { + return Ok(result); + } + let batch = if owned_operations.iter().any(|(_, saved)| saved.is_none()) { + let tx: Value = self + .rpc + .call("eth_getTransactionByHash", json!([hash])) + .await?; + let input: Bytes = serde_json::from_value(tx["input"].clone())?; + let target: Option
= serde_json::from_value(tx["to"].clone())?; + // A wrapper's outer calldata need not describe the operation it executes. + if target == Some(ENTRY_POINT) { + EntryPoint::handleOpsCall::abi_decode(&input).ok() + } else { + None + } + } else { + None + }; + for (event, saved) in owned_operations { + let operation_hash = format!("{:#x}", event.userOpHash); + let (recipient, amount, destination) = if let Some(saved) = saved { + (saved.recipient, saved.amount, saved.destination) + } else { + let Some(op) = batch.as_ref().and_then(|batch| { + batch + .ops + .iter() + .find(|op| op.sender == self.address && op.nonce == event.nonce) + }) else { + continue; + }; + let Some((recipient, amount, destination)) = decode_payment(&op.callData)? else { + continue; + }; + (recipient.to_checksum(None), amount, destination) + }; + let mut transfer = UsdtTransfer { + id: operation_hash.clone(), + tx_hash: hash.into(), + user_operation_hash: Some(operation_hash), + bridge_guid: None, + recipient, + destination, + amount, + received_amount: amount, + bridge_fee: 0, + fee: None, + is_incoming: false, + status: UsdtTransferStatus::Pending, + timestamp, + explorer_url: format!("{EXPLORER}/tx/{hash}"), + }; + self.settle(&mut transfer, receipt, event.success)?; + let operation_logs = super::transaction::operation_logs(receipt, event.userOpHash)?; + let outgoing_ids: Vec<_> = operation_logs + .iter() + .filter_map(|log| { + let index: U256 = serde_json::from_value(log["logIndex"].clone()).ok()?; + Some(format!("{hash}:{index}")) + }) + .collect(); + result.retain(|transfer| transfer.is_incoming || !outgoing_ids.contains(&transfer.id)); + result.push(transfer); + } + Ok(result) + } +} + +fn decode_payment(data: &[u8]) -> Result, UsdtError> { + let Ok(calls) = decode_calls(data) else { + return Ok(None); + }; + let mut payment = None; + let mut payment_count = 0; + let mut supported = true; + for (target, data) in calls { + if target == TOKEN { + if let Ok(call) = Erc20::transferCall::abi_decode(&data) { + payment_count += 1; + payment = Some(( + call.recipient, + token_amount(call.amount)?, + UsdtDestination::Arbitrum, + )); + } else if Erc20::approveCall::abi_decode(&data).is_err() { + supported = false; + } + } else if target == BRIDGE_HELPER { + if let Ok(call) = BridgeHelper::sendCall::abi_decode(&data) { + if call.oft != OFT { + supported = false; + continue; + } + let Some(destination) = [ + UsdtDestination::Ethereum, + UsdtDestination::Polygon, + UsdtDestination::Plasma, + UsdtDestination::Stable, + ] + .into_iter() + .find(|d| d.endpoint() == Some(call.param.dstEid)) else { + supported = false; + continue; + }; + payment_count += 1; + payment = Some(( + Address::from_word(call.param.to), + token_amount(call.param.amountLD)?, + destination, + )); + } else { + supported = false; + } + } else { + supported = false; + } + } + if !supported || payment_count != 1 { + return Ok(None); + } + Ok(payment) +} + +pub(super) fn decode_calls(data: &[u8]) -> Result, UsdtError> { + if let Ok(execute) = SimpleAccount::executeCall::abi_decode(data) { + if !execute.value.is_zero() { + return Err(UsdtError::InvalidResponse); + } + return Ok(vec![(execute.target, execute.data)]); + } + let batch = SimpleAccount::executeBatchCall::abi_decode(data) + .map_err(|_| UsdtError::InvalidResponse)?; + batch + .calls + .into_iter() + .map(|call| { + if !call.value.is_zero() { + return Err(UsdtError::InvalidResponse); + } + Ok((call.target, call.data)) + }) + .collect() +} diff --git a/src/modules/usdt/keys.rs b/src/modules/usdt/keys.rs new file mode 100644 index 0000000..94f62f8 --- /dev/null +++ b/src/modules/usdt/keys.rs @@ -0,0 +1,68 @@ +use super::UsdtError; +use alloy_primitives::{keccak256, Address}; +use bip39::Mnemonic; +use bitcoin::{ + bip32::{DerivationPath, Xpriv}, + secp256k1::{Secp256k1, SecretKey}, +}; +use std::str::FromStr; +use zeroize::Zeroizing; + +pub(super) struct SigningKey(SecretKey); +impl std::ops::Deref for SigningKey { + type Target = SecretKey; + fn deref(&self) -> &SecretKey { + &self.0 + } +} +impl Drop for SigningKey { + fn drop(&mut self) { + self.0.non_secure_erase(); + } +} + +pub(super) fn derive_key( + mnemonic: String, + passphrase: Option, +) -> Result { + let phrase = Zeroizing::new(mnemonic); + let passphrase = Zeroizing::new(passphrase.unwrap_or_default()); + let mnemonic = Mnemonic::parse(&*phrase).map_err(|_| UsdtError::InvalidCredentials)?; + let seed = Zeroizing::new(mnemonic.to_seed(passphrase.as_str())); + let mut root = Xpriv::new_master(bitcoin::Network::Bitcoin, seed.as_ref()) + .map_err(|_| UsdtError::InvalidCredentials)?; + let path = + DerivationPath::from_str("m/44'/60'/0'/0/0").map_err(|_| UsdtError::InvalidCredentials)?; + let derived = root.derive_priv(&Secp256k1::new(), &path); + root.private_key.non_secure_erase(); + Ok(SigningKey( + derived + .map_err(|_| UsdtError::InvalidCredentials)? + .private_key, + )) +} + +pub(super) fn key_address(key: &SecretKey) -> Address { + let public = key.public_key(&Secp256k1::new()).serialize_uncompressed(); + Address::from_slice(&keccak256(&public[1..])[12..]) +} + +#[uniffi::export] +pub fn usdt_address(mnemonic: String, passphrase: Option) -> Result { + Ok(key_address(&*derive_key(mnemonic, passphrase)?).to_checksum(None)) +} + +pub(super) fn parse_address(value: &str) -> Result { + let address = Address::from_str(value).map_err(|_| UsdtError::InvalidAddress)?; + if value.len() != 42 || !value.starts_with("0x") || address == Address::ZERO { + return Err(UsdtError::InvalidAddress); + } + let body = &value[2..]; + if body.bytes().any(|c| c.is_ascii_lowercase()) + && body.bytes().any(|c| c.is_ascii_uppercase()) + && address.to_checksum(None) != value + { + return Err(UsdtError::InvalidAddress); + } + Ok(address) +} diff --git a/src/modules/usdt/mod.rs b/src/modules/usdt/mod.rs new file mode 100644 index 0000000..f324cbf --- /dev/null +++ b/src/modules/usdt/mod.rs @@ -0,0 +1,25 @@ +mod account; +mod amount; +mod deposits; +mod errors; +mod history; +mod keys; +mod paymaster; +mod payment_request; +mod rpc; +mod store; +mod transaction; +mod types; +mod user_operation; +mod wallet; + +pub use amount::{usdt_format_amount, usdt_parse_amount}; +pub use deposits::*; +pub use errors::UsdtError; +pub use keys::usdt_address; +pub use payment_request::usdt_parse_payment_request; +pub use types::*; +pub use wallet::UsdtWallet; + +#[cfg(test)] +mod tests; diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs new file mode 100644 index 0000000..bbe90cf --- /dev/null +++ b/src/modules/usdt/paymaster.rs @@ -0,0 +1,387 @@ +use super::{ + account::ENTRY_POINT, + rpc::Rpc, + transaction::Erc20, + user_operation::{Authorization, UserOperation}, + UsdtError, +}; +use alloy_primitives::{address, Address, Bytes, U256}; +use alloy_sol_types::SolCall; +use serde::Deserialize; +use serde_json::json; + +use super::types::{CHAIN_ID as ARBITRUM_CHAIN_ID, TOKEN as USDT0}; +pub(super) const PAYMASTER: Address = address!("888888888888Ec68A58AB8094Cc1AD20Ba3D2402"); + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct TokenQuote { + paymaster: Address, + token: Address, + post_op_gas: U256, + exchange_rate: U256, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct PaymasterData { + paymaster: Address, + paymaster_data: Bytes, + paymaster_verification_gas_limit: Option, + paymaster_post_op_gas_limit: Option, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GasEstimate { + call_gas_limit: U256, + verification_gas_limit: U256, + pre_verification_gas: U256, + paymaster_verification_gas_limit: Option, + paymaster_post_op_gas_limit: Option, +} + +impl GasEstimate { + fn apply(&self, op: &mut UserOperation) -> Result { + let mut changed = false; + // Pre-verification estimates also vary with Arbitrum's L1 data cost. + for (limit, estimate) in [ + (&mut op.call_gas_limit, self.call_gas_limit), + (&mut op.pre_verification_gas, self.pre_verification_gas), + ] { + if estimate > *limit { + *limit = estimate + .checked_add(estimate / U256::from(10)) + .and_then(|value| value.checked_add(U256::from(1))) + .ok_or(UsdtError::InvalidResponse)?; + changed = true; + } + } + for (limit, estimate) in [ + (&mut op.verification_gas_limit, self.verification_gas_limit), + ( + &mut op.paymaster_verification_gas_limit, + self.paymaster_verification_gas_limit.unwrap_or_default(), + ), + ( + &mut op.paymaster_post_op_gas_limit, + self.paymaster_post_op_gas_limit.unwrap_or_default(), + ), + ] { + if estimate > *limit { + *limit = estimate; + changed = true; + } + } + Ok(changed) + } +} + +pub(super) struct Pimlico { + pub rpc: Rpc, +} + +impl Pimlico { + pub async fn prepare( + &self, + address: Address, + nonce: U256, + authorization: Authorization, + calls: &[(Address, Bytes)], + timestamp: u64, + ) -> Result<(UserOperation, u64, u64), UsdtError> { + self.rpc.verify_chain().await?; + #[derive(Deserialize)] + struct Quotes { + quotes: Vec, + } + #[derive(Deserialize)] + struct Prices { + fast: GasPrice, + } + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct GasPrice { + max_fee_per_gas: U256, + max_priority_fee_per_gas: U256, + } + let quotes: Quotes = self + .rpc + .call( + "pimlico_getTokenQuotes", + json!([{ "tokens": [USDT0] }, ENTRY_POINT, U256::from(ARBITRUM_CHAIN_ID)]), + ) + .await?; + let quote = quotes + .quotes + .into_iter() + .find(|quote| { + quote.token == USDT0 + && quote.paymaster == PAYMASTER + && !quote.exchange_rate.is_zero() + }) + .ok_or(UsdtError::UnsupportedRoute)?; + let price: Prices = self + .rpc + .call("pimlico_getUserOperationGasPrice", json!([])) + .await?; + if price.fast.max_fee_per_gas.is_zero() + || price.fast.max_priority_fee_per_gas > price.fast.max_fee_per_gas + { + return Err(UsdtError::InvalidResponse); + } + let dummy_signature = Bytes::from_static(&alloy_primitives::hex!("fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c")); + let mut op = UserOperation { + sender: address, + nonce, + factory: Bytes::from_static(&[0x77, 0x02]), + factory_data: Bytes::new(), + call_data: with_approval(calls, U256::MAX), + call_gas_limit: U256::ZERO, + verification_gas_limit: U256::ZERO, + pre_verification_gas: U256::ZERO, + max_fee_per_gas: price.fast.max_fee_per_gas, + max_priority_fee_per_gas: price.fast.max_priority_fee_per_gas, + paymaster: PAYMASTER, + paymaster_verification_gas_limit: U256::ZERO, + paymaster_post_op_gas_limit: U256::ZERO, + paymaster_data: Bytes::new(), + signature: dummy_signature, + eip7702_auth: authorization, + }; + self.apply_paymaster(&mut op, "pm_getPaymasterStubData") + .await?; + let estimate: GasEstimate = self + .rpc + .call("eth_estimateUserOperationGas", json!([op, ENTRY_POINT])) + .await?; + estimate.apply(&mut op)?; + let estimate_terms = Terms { + exchange_rate: quote.exchange_rate, + post_op_gas: quote.post_op_gas, + constant_fee: U256::ZERO, + valid_until: 0, + valid_after: 0, + }; + let mut allowance = approval_margin(estimate_terms.maximum_token_cost(&op)?)?; + // Final provider data can include a fixed token charge. Rebuild the bounded approval before signing. + for _ in 0..3 { + op.call_data = with_approval(calls, allowance); + self.apply_paymaster(&mut op, "pm_getPaymasterData").await?; + let terms = Terms::decode(&op.paymaster_data)?; + let estimate: GasEstimate = self + .rpc + .call("eth_estimateUserOperationGas", json!([op, ENTRY_POINT])) + .await?; + let gas_changed = estimate.apply(&mut op)?; + let required = terms.maximum_token_cost(&op)?; + if gas_changed || required > allowance { + allowance = allowance.max(approval_margin(required)?); + continue; + } + if terms.valid_until == 0 || terms.valid_until > timestamp.saturating_add(900) { + return Err(UsdtError::InvalidResponse); + } + if terms.valid_after > timestamp || terms.valid_until <= timestamp.saturating_add(15) { + return Err(UsdtError::QuoteExpired); + } + return Ok(( + op, + required + .try_into() + .map_err(|_| UsdtError::InvalidResponse)?, + terms.valid_until, + )); + } + Err(UsdtError::QuoteExpired) + } + + async fn apply_paymaster(&self, op: &mut UserOperation, method: &str) -> Result<(), UsdtError> { + let data: PaymasterData = self + .rpc + .call( + method, + json!([op, ENTRY_POINT, U256::from(ARBITRUM_CHAIN_ID), {"token": USDT0}]), + ) + .await?; + if data.paymaster != PAYMASTER + || (method == "pm_getPaymasterStubData" && data.paymaster_post_op_gas_limit.is_none()) + { + return Err(UsdtError::InvalidResponse); + } + op.paymaster_data = data.paymaster_data; + if let Some(gas) = data.paymaster_verification_gas_limit { + op.paymaster_verification_gas_limit = gas; + } + if let Some(gas) = data.paymaster_post_op_gas_limit { + op.paymaster_post_op_gas_limit = gas; + } + Ok(()) + } + + pub async fn validate_gas(&self, op: &UserOperation) -> Result<(), UsdtError> { + let estimate: GasEstimate = self + .rpc + .call("eth_estimateUserOperationGas", json!([op, ENTRY_POINT])) + .await?; + if estimate.call_gas_limit > op.call_gas_limit + || estimate.verification_gas_limit > op.verification_gas_limit + || estimate.pre_verification_gas > op.pre_verification_gas + || estimate + .paymaster_verification_gas_limit + .is_some_and(|gas| gas > op.paymaster_verification_gas_limit) + || estimate + .paymaster_post_op_gas_limit + .is_some_and(|gas| gas > op.paymaster_post_op_gas_limit) + { + return Err(UsdtError::QuoteExpired); + } + Ok(()) + } +} + +fn with_approval(calls: &[(Address, Bytes)], amount: U256) -> Bytes { + let mut batch = vec![( + USDT0, + Erc20::approveCall { + spender: PAYMASTER, + amount, + } + .abi_encode() + .into(), + )]; + batch.extend_from_slice(calls); + super::account::batch(&batch) +} + +fn approval_margin(value: U256) -> Result { + value + .checked_add(value / U256::from(20)) + .and_then(|value| value.checked_add(U256::from(1))) + .ok_or(UsdtError::InvalidResponse) +} + +pub(super) fn with_margin(value: U256) -> Result { + value + .checked_add(value / U256::from(5)) + .and_then(|value| value.checked_add(U256::from(1))) + .ok_or(UsdtError::InvalidResponse) +} + +struct Terms { + exchange_rate: U256, + post_op_gas: U256, + constant_fee: U256, + valid_until: u64, + valid_after: u64, +} + +impl Terms { + // SingletonPaymasterV8 inherits the V7 token-fee encoding. + fn decode(data: &[u8]) -> Result { + if data.len() < 182 + || data[0] >> 1 != 1 + || data[1] & !1 != 0 + || Address::from_slice(&data[14..34]) != USDT0 + { + return Err(UsdtError::InvalidResponse); + } + // Pre-funding needs a separate approval; recipient mode can collect unused prefund. Neither is used. + let constant_present = data[1] & 1 != 0; + let signature_start = 118 + if constant_present { 16 } else { 0 }; + if !matches!(data.len().checked_sub(signature_start), Some(64 | 65)) { + return Err(UsdtError::InvalidResponse); + } + let terms = Self { + valid_until: U256::from_be_slice(&data[2..8]) + .try_into() + .map_err(|_| UsdtError::InvalidResponse)?, + valid_after: U256::from_be_slice(&data[8..14]) + .try_into() + .map_err(|_| UsdtError::InvalidResponse)?, + post_op_gas: U256::from_be_slice(&data[34..50]), + exchange_rate: U256::from_be_slice(&data[50..82]), + constant_fee: if constant_present { + U256::from_be_slice(&data[118..134]) + } else { + U256::ZERO + }, + }; + if terms.exchange_rate.is_zero() { + return Err(UsdtError::InvalidResponse); + } + Ok(terms) + } + + fn maximum_token_cost(&self, op: &UserOperation) -> Result { + let overhead = self + .post_op_gas + .checked_mul(op.max_fee_per_gas) + .ok_or(UsdtError::InvalidResponse)?; + let native = op + .maximum_native_cost()? + .checked_add(overhead) + .ok_or(UsdtError::InvalidResponse)?; + let scaled = native + .checked_mul(self.exchange_rate) + .ok_or(UsdtError::InvalidResponse)?; + let divisor = U256::from(1_000_000_000_000_000_000u64); + let rounded = scaled / divisor + U256::from(u8::from(scaled % divisor != U256::ZERO)); + rounded + .checked_add(self.constant_fee) + .ok_or(UsdtError::InvalidResponse) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn gas_margins_reject_overflow() { + let vector: serde_json::Value = + serde_json::from_str(include_str!("fixtures/eip7702-vectors.json")).unwrap(); + for (call, pre_verification) in [(U256::MAX, U256::ZERO), (U256::ZERO, U256::MAX)] { + let mut op: UserOperation = + serde_json::from_value(vector["operation"].clone()).unwrap(); + let estimate = GasEstimate { + call_gas_limit: call, + verification_gas_limit: U256::ZERO, + pre_verification_gas: pre_verification, + paymaster_verification_gas_limit: None, + paymaster_post_op_gas_limit: None, + }; + assert!(matches!( + estimate.apply(&mut op), + Err(UsdtError::InvalidResponse) + )); + } + } + + #[test] + fn token_fee_includes_fixed_charge_and_rejects_other_collection_modes() { + let mut data = vec![0; 118]; + data[0] = 3; + data[1] = 1; + data[14..34].copy_from_slice(USDT0.as_slice()); + data[34..50].copy_from_slice(&50_000u128.to_be_bytes()); + data[50..82].copy_from_slice(&U256::from(3_000_000_000u64).to_be_bytes::<32>()); + data.extend_from_slice(&123u128.to_be_bytes()); + data.extend_from_slice(&[1; 65]); + let vector: serde_json::Value = + serde_json::from_str(include_str!("fixtures/eip7702-vectors.json")).unwrap(); + let op: UserOperation = serde_json::from_value(vector["operation"].clone()).unwrap(); + let terms = Terms::decode(&data).unwrap(); + assert_eq!(terms.maximum_token_cost(&op).unwrap(), U256::from(102_123)); + for flags in [2, 4, 8] { + data[1] = flags; + assert!(Terms::decode(&data).is_err()); + } + data[1] = 1; + data[14] ^= 1; + assert!(Terms::decode(&data).is_err()); + for length in 0..182 { + assert!(Terms::decode(&data[..length]).is_err()); + } + } +} diff --git a/src/modules/usdt/payment_request.rs b/src/modules/usdt/payment_request.rs new file mode 100644 index 0000000..89d2fba --- /dev/null +++ b/src/modules/usdt/payment_request.rs @@ -0,0 +1,54 @@ +use super::{ + keys::parse_address, + types::{CHAIN_ID, TOKEN}, + UsdtError, UsdtPaymentRequest, +}; +use alloy_primitives::Address; + +#[uniffi::export] +pub fn usdt_parse_payment_request(value: String) -> Result { + let (recipient, amount) = parse_request(&value)?; + Ok(UsdtPaymentRequest { + recipient: recipient.to_checksum(None), + amount, + }) +} + +fn parse_request(value: &str) -> Result<(Address, Option), UsdtError> { + let value = value.trim(); + if value.len() > 2048 { + return Err(UsdtError::InvalidAddress); + } + let Some(uri) = value.strip_prefix("ethereum:") else { + return Ok((parse_address(value)?, None)); + }; + let (target, query) = uri.split_once('?').unwrap_or((uri, "")); + let (address, chain) = target + .strip_prefix("pay-") + .unwrap_or(target) + .split_once('@') + .ok_or(UsdtError::WrongNetwork)?; + let Some(chain) = chain.strip_suffix("/transfer") else { + if chain != CHAIN_ID.to_string() || !query.is_empty() { + return Err(UsdtError::WrongNetwork); + } + return Ok((parse_address(address)?, None)); + }; + if chain != CHAIN_ID.to_string() || parse_address(address)? != TOKEN { + return Err(UsdtError::WrongNetwork); + } + let mut recipient = None; + let mut amount = None; + // ERC-681 uses a literal plus for positive numbers, not HTML form spaces. + let query = query.replace('+', "%2B"); + for (key, value) in url::form_urlencoded::parse(query.as_bytes()) { + match key.as_ref() { + "address" if recipient.is_none() => recipient = Some(parse_address(&value)?), + "uint256" if amount.is_none() => { + amount = Some(super::amount::parse_atomic_amount(&value)?) + } + _ => return Err(UsdtError::InvalidAddress), + } + } + Ok((recipient.ok_or(UsdtError::InvalidAddress)?, amount)) +} diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs new file mode 100644 index 0000000..f43522e --- /dev/null +++ b/src/modules/usdt/rpc.rs @@ -0,0 +1,252 @@ +use super::{UsdtError, UsdtTransfer, UsdtTransferStatus}; +use alloy_primitives::{Address, Bytes, U256}; +use alloy_sol_types::SolCall; +use serde::{de::DeserializeOwned, Deserialize}; +use serde_json::{json, Value}; +use std::{sync::Arc, time::Duration}; +use tokio::{sync::Mutex, time::Instant}; + +const REQUEST_INTERVAL: Duration = Duration::from_millis(750); +const BURST_WINDOW: Duration = Duration::from_millis(750 * 19); + +pub(super) struct Rpc { + client: reqwest::Client, + #[cfg(test)] + pub(super) bridge_status_url: Option, + url: String, + chain_id: u64, + next_request: Arc>, +} + +#[derive(Deserialize)] +struct Response { + result: Option, + error: Option, +} +#[derive(Deserialize)] +struct RpcError { + code: i64, + message: String, +} + +impl Rpc { + pub fn new(url: String, chain_id: u64) -> Result { + let parsed = url::Url::parse(&url).map_err(|_| UsdtError::NetworkUnavailable)?; + if parsed.scheme() != "https" + && !(parsed.scheme() == "http" + && matches!(parsed.host_str(), Some("127.0.0.1" | "localhost"))) + { + return Err(UsdtError::NetworkUnavailable); + } + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(25)) + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(|_| UsdtError::NetworkUnavailable)?; + Ok(Self { + client, + #[cfg(test)] + bridge_status_url: None, + url, + chain_id, + next_request: Arc::new(Mutex::new(Instant::now())), + }) + } + + pub fn with_url(&self, url: String) -> Result { + let mut rpc = Self::new(url, self.chain_id)?; + rpc.next_request = self.next_request.clone(); + Ok(rpc) + } + + pub async fn verify_chain(&self) -> Result<(), UsdtError> { + let chain: U256 = self.call("eth_chainId", json!([])).await?; + if chain != U256::from(self.chain_id) { + return Err(UsdtError::WrongNetwork); + } + Ok(()) + } + + pub async fn call( + &self, + method: &str, + params: Value, + ) -> Result { + self.reserve_request().await; + let response = self + .client + .post(&self.url) + .json(&json!({"jsonrpc":"2.0","id":1,"method":method,"params":params})) + .send() + .await + .map_err(|_| UsdtError::NetworkUnavailable)?; + if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS { + return Err(UsdtError::RateLimited); + } + let response = response + .error_for_status() + .map_err(|_| UsdtError::NetworkUnavailable)?; + let overflow = if method == "eth_getLogs" { + UsdtError::LogRangeTooLarge + } else { + UsdtError::InvalidResponse + }; + let response: Response = + serde_json::from_value(Self::bounded_json(response, overflow).await?)?; + if let Some(error) = response.error { + let message = error.message.to_ascii_lowercase(); + if error.code == -32016 + || [ + "rate limit", + "quota", + "credit", + "too many requests", + "requests per", + "compute units", + ] + .iter() + .any(|term| message.contains(term)) + { + return Err(UsdtError::RateLimited); + } + if method == "eth_getLogs" && error.code == -32005 { + return Err(UsdtError::LogRangeTooLarge); + } + if message.contains("insufficient funds") || message.contains("insufficient balance") { + return Err(UsdtError::InsufficientBalance); + } + return Err(UsdtError::TransactionRejected { + reason: error.message.chars().take(200).collect(), + }); + } + serde_json::from_value(response.result.unwrap_or(Value::Null)).map_err(Into::into) + } + + async fn reserve_request(&self) { + let mut next = self.next_request.lock().await; + let scheduled = (*next).max(Instant::now()); + // Allow 20 requests in a burst, then 80/minute across chain and bundler calls. + tokio::time::sleep_until(scheduled - BURST_WINDOW).await; + *next = scheduled + REQUEST_INTERVAL; + } + + pub async fn bridge_status( + &self, + transfer: &UsdtTransfer, + ) -> Result { + if transfer.bridge_guid.is_none() { + return Ok(transfer.status); + } + let url = format!( + "https://scan.layerzero-api.com/v1/messages/tx/{}", + transfer.tx_hash + ); + #[cfg(test)] + let url = self + .bridge_status_url + .as_ref() + .map(|base| format!("{base}/{}", transfer.tx_hash)) + .unwrap_or(url); + let response = self + .client + .get(url) + .send() + .await + .map_err(|_| UsdtError::NetworkUnavailable)? + .error_for_status() + .map_err(|_| UsdtError::NetworkUnavailable)?; + let response = Self::bounded_json(response, UsdtError::InvalidResponse).await?; + let messages = response["data"] + .as_array() + .ok_or(UsdtError::InvalidResponse)?; + let message = messages.iter().find(|message| { + message["guid"].as_str().is_some_and(|guid| { + transfer + .bridge_guid + .as_ref() + .is_some_and(|expected| guid.eq_ignore_ascii_case(expected)) + }) && message["pathway"]["srcEid"].as_u64() == Some(30110) + && message["pathway"]["dstEid"].as_u64() + == transfer.destination.endpoint().map(u64::from) + && message["pathway"]["sender"]["address"] + .as_str() + .is_some_and(|a| a.eq_ignore_ascii_case(&super::types::OFT.to_string())) + && message["source"]["tx"]["txHash"] + .as_str() + .is_some_and(|h| h.eq_ignore_ascii_case(&transfer.tx_hash)) + }); + Ok(match message.and_then(|m| m["status"]["name"].as_str()) { + Some("DELIVERED") => UsdtTransferStatus::Confirmed, + Some( + "FAILED" + | "BLOCKED" + | "PAYLOAD_STORED" + | "APPLICATION_BURNED" + | "APPLICATION_SKIPPED", + ) => UsdtTransferStatus::BridgeNeedsAttention, + _ => transfer.status, + }) + } + + async fn bounded_json( + mut response: reqwest::Response, + overflow: UsdtError, + ) -> Result { + let mut body = Vec::new(); + while let Some(chunk) = response + .chunk() + .await + .map_err(|_| UsdtError::NetworkUnavailable)? + { + if body.len() + chunk.len() > 2_097_152 { + return Err(overflow); + } + body.extend_from_slice(&chunk); + } + serde_json::from_slice(&body).map_err(Into::into) + } + + pub async fn balance(&self, address: Address) -> Result { + self.call("eth_getBalance", json!([address, "pending"])) + .await + } + + pub async fn contract(&self, to: Address, call: C) -> Result { + let bytes: Bytes = self + .call( + "eth_call", + json!([{"to":to,"data":Bytes::from(call.abi_encode())},"latest"]), + ) + .await?; + C::abi_decode_returns(&bytes).map_err(|_| UsdtError::InvalidResponse) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test(start_paused = true)] + async fn chain_and_bundler_share_bursts_and_sustained_budget() { + let chain = Rpc::new("https://chain.example".into(), 42161).unwrap(); + let bundler = chain.with_url("https://bundler.example".into()).unwrap(); + let start = Instant::now(); + for _ in 0..10 { + chain.reserve_request().await; + bundler.reserve_request().await; + } + assert_eq!(Instant::now(), start); + for _ in 0..80 { + chain.reserve_request().await; + } + assert_eq!(Instant::now() - start, Duration::from_secs(60)); + tokio::time::sleep(Duration::from_secs(30)).await; + let idle = Instant::now(); + for _ in 0..20 { + bundler.reserve_request().await; + } + assert_eq!(Instant::now(), idle); + chain.reserve_request().await; + assert_eq!(Instant::now() - idle, REQUEST_INTERVAL); + } +} diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs new file mode 100644 index 0000000..9c5d052 --- /dev/null +++ b/src/modules/usdt/store.rs @@ -0,0 +1,303 @@ +use super::{transaction::Plan, UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus}; +use rusqlite::{params, Connection, OptionalExtension, Transaction}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use std::{ + path::Path, + sync::{Mutex, MutexGuard}, +}; + +#[derive(Clone, Serialize, Deserialize)] +pub(super) struct QuoteData { + pub quote: UsdtQuote, + pub plan: Plan, +} + +pub(super) struct Store(Mutex); + +impl Store { + pub fn open(path: &str, identity: &str) -> Result { + if let Some(parent) = Path::new(path).parent() { + std::fs::create_dir_all(parent).map_err(|e| UsdtError::Storage { + reason: e.to_string(), + })?; + } + let connection = Connection::open(path)?; + connection.busy_timeout(std::time::Duration::from_secs(5))?; + connection.execute_batch("PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; + CREATE TABLE IF NOT EXISTS usdt_identity (id INTEGER PRIMARY KEY CHECK(id=1), identity TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS usdt_sync (id INTEGER PRIMARY KEY CHECK(id=1), newest INTEGER NOT NULL); + CREATE TABLE IF NOT EXISTS usdt_history_progress (id INTEGER PRIMARY KEY CHECK(id=1), next INTEGER NOT NULL); + CREATE TABLE IF NOT EXISTS usdt_history_receipts (hash TEXT PRIMARY KEY); + CREATE TABLE IF NOT EXISTS usdt_quotes (id TEXT PRIMARY KEY, data TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS usdt_transfers (id TEXT PRIMARY KEY, hash TEXT NOT NULL, raw TEXT, data TEXT NOT NULL);")?; + connection.execute( + "INSERT OR IGNORE INTO usdt_identity VALUES (1,?1)", + [identity], + )?; + let stored: String = + connection.query_row("SELECT identity FROM usdt_identity WHERE id=1", [], |r| { + r.get(0) + })?; + if stored != identity { + return Err(UsdtError::InvalidCredentials); + } + Ok(Self(Mutex::new(connection))) + } + + fn connection(&self) -> Result, UsdtError> { + self.0.lock().map_err(|_| UsdtError::Storage { + reason: "USDT storage lock unavailable".into(), + }) + } + + pub fn save_quote(&self, data: &QuoteData) -> Result<(), UsdtError> { + let mut connection = self.connection()?; + let tx = connection.transaction()?; + tx.execute( + "DELETE FROM usdt_quotes WHERE json_extract(data, '$.quote.expires_at') <= ?1", + [super::wallet::now()], + )?; + tx.execute( + "INSERT INTO usdt_quotes VALUES (?1,?2)", + params![data.quote.id, serde_json::to_string(data)?], + )?; + tx.commit()?; + Ok(()) + } + + pub fn quote(&self, id: &str) -> Result { + let data: Option = self + .connection()? + .query_row("SELECT data FROM usdt_quotes WHERE id=?1", [id], |r| { + r.get(0) + }) + .optional()?; + decode(&data.ok_or(UsdtError::QuoteExpired)?) + } + + pub fn transfer(&self, id: &str) -> Result, UsdtError> { + let data: Option = self + .connection()? + .query_row("SELECT data FROM usdt_transfers WHERE id=?1", [id], |r| { + r.get(0) + }) + .optional()?; + data.map(|data| decode(&data)).transpose() + } + + pub fn transfer_by_hash(&self, hash: &str) -> Result, UsdtError> { + let data: Option = self + .connection()? + .query_row( + "SELECT data FROM usdt_transfers WHERE hash=?1", + [hash], + |r| r.get(0), + ) + .optional()?; + data.map(|data| decode(&data)).transpose() + } + + pub fn transfers(&self) -> Result, UsdtError> { + let connection = self.connection()?; + let mut statement = connection.prepare( + "SELECT data FROM usdt_transfers ORDER BY json_extract(data, '$.timestamp') DESC", + )?; + let rows = statement.query_map([], |row| row.get::<_, String>(0))?; + rows.map(|row| decode(&row?)).collect() + } + + pub fn require_no_pending(&self) -> Result<(), UsdtError> { + require_no_pending(&*self.connection()?) + } + + pub fn record_signed(&self, transfer: &UsdtTransfer, raw: &str) -> Result<(), UsdtError> { + let mut connection = self.connection()?; + let tx = connection.transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)?; + require_no_pending(&tx)?; + tx.execute( + "INSERT INTO usdt_transfers (id,hash,raw,data) VALUES (?1,?2,?3,?4)", + params![ + transfer.id, + transfer.user_operation_hash, + raw, + serde_json::to_string(transfer)? + ], + )?; + tx.execute("DELETE FROM usdt_quotes WHERE id=?1", [&transfer.id])?; + tx.commit()?; + Ok(()) + } + + pub fn update_transfer(&self, transfer: &UsdtTransfer) -> Result<(), UsdtError> { + let settled = matches!( + transfer.status, + UsdtTransferStatus::Confirmed + | UsdtTransferStatus::Failed + | UsdtTransferStatus::Replaced + | UsdtTransferStatus::Bridging + | UsdtTransferStatus::BridgeNeedsAttention + ); + self.connection()?.execute("UPDATE usdt_transfers SET data=?1, raw=CASE WHEN ?2 THEN NULL ELSE raw END WHERE id=?3", + params![serde_json::to_string(transfer)?, settled, transfer.id])?; + Ok(()) + } + + pub fn synced_block(&self) -> Result, UsdtError> { + Ok(self + .connection()? + .query_row("SELECT newest FROM usdt_sync WHERE id=1", [], |row| { + row.get(0) + }) + .optional()?) + } + + pub fn complete_history(&self, newest: u64) -> Result<(), UsdtError> { + let mut connection = self.connection()?; + let tx = connection.transaction()?; + tx.execute("DELETE FROM usdt_history_progress", [])?; + tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.execute("INSERT INTO usdt_sync (id,newest) VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET newest=excluded.newest", [newest])?; + tx.commit()?; + Ok(()) + } + + pub fn history_progress(&self) -> Result, UsdtError> { + Ok(self + .connection()? + .query_row( + "SELECT next FROM usdt_history_progress WHERE id=1", + [], + |row| row.get(0), + ) + .optional()?) + } + + pub fn save_history_progress(&self, next: u64) -> Result<(), UsdtError> { + let mut connection = self.connection()?; + let tx = connection.transaction()?; + tx.execute("INSERT INTO usdt_history_progress VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET next=excluded.next", [next])?; + tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.commit()?; + Ok(()) + } + + pub fn transaction_timestamp(&self, hash: &str) -> Result, UsdtError> { + Ok(self.connection()?.query_row( + "SELECT json_extract(data, '$.timestamp') FROM usdt_transfers WHERE json_extract(data, '$.tx_hash')=?1 LIMIT 1", + [hash], |row| row.get(0), + ).optional()?) + } + + pub fn has_history_receipt(&self, hash: &str) -> Result { + Ok(self.connection()?.query_row( + "SELECT EXISTS(SELECT 1 FROM usdt_history_receipts WHERE hash=?1)", + [hash], + |row| row.get(0), + )?) + } + + pub fn save_history_receipt( + &self, + transfers: &[UsdtTransfer], + hash: &str, + ) -> Result<(), UsdtError> { + let mut connection = self.connection()?; + let tx = connection.transaction()?; + Self::merge_history(&tx, transfers)?; + tx.execute( + "INSERT OR IGNORE INTO usdt_history_receipts VALUES (?1)", + [hash], + )?; + tx.commit()?; + Ok(()) + } + + fn merge_history(tx: &Transaction<'_>, transfers: &[UsdtTransfer]) -> Result<(), UsdtError> { + for transfer in transfers { + let hash = transfer + .user_operation_hash + .as_deref() + .unwrap_or(&transfer.id); + let existing: Option = tx + .query_row( + "SELECT data FROM usdt_transfers WHERE hash=?1", + [hash], + |row| row.get(0), + ) + .optional()?; + let mut transfer = transfer.clone(); + if let Some(data) = existing { + let saved: UsdtTransfer = decode(&data)?; + transfer.id = saved.id; + if saved.tx_hash == transfer.tx_hash + && saved.bridge_guid == transfer.bridge_guid + && transfer.status == UsdtTransferStatus::Bridging + && matches!( + saved.status, + UsdtTransferStatus::Confirmed | UsdtTransferStatus::BridgeNeedsAttention + ) + { + transfer.status = saved.status; + } + tx.execute( + "UPDATE usdt_transfers SET data=?1, raw=NULL WHERE id=?2", + params![serde_json::to_string(&transfer)?, transfer.id], + )?; + } else { + tx.execute( + "INSERT INTO usdt_transfers (id,hash,data) VALUES (?1,?2,?3)", + params![transfer.id, hash, serde_json::to_string(&transfer)?], + )?; + } + } + Ok(()) + } + + pub fn unsettled(&self) -> Result, UsdtError> { + let connection = self.connection()?; + let mut statement = connection.prepare("SELECT data FROM usdt_transfers")?; + let rows = statement.query_map([], |row| row.get::<_, String>(0))?; + let mut result = Vec::new(); + for row in rows { + let transfer: UsdtTransfer = decode(&row?)?; + if matches!( + transfer.status, + UsdtTransferStatus::Pending + | UsdtTransferStatus::Bridging + | UsdtTransferStatus::BridgeNeedsAttention + ) { + result.push(transfer); + } + } + Ok(result) + } + + pub fn pending_plan(&self, id: &str) -> Result, UsdtError> { + let raw: Option = self + .connection()? + .query_row("SELECT raw FROM usdt_transfers WHERE id=?1", [id], |r| { + r.get(0) + }) + .optional()? + .flatten(); + raw.map(|raw| decode(&raw)).transpose() + } +} + +fn decode(data: &str) -> Result { + serde_json::from_str(data).map_err(|error| UsdtError::Storage { + reason: error.to_string(), + }) +} + +fn require_no_pending(connection: &Connection) -> Result<(), UsdtError> { + let pending: bool = connection.query_row( + "SELECT EXISTS(SELECT 1 FROM usdt_transfers WHERE raw IS NOT NULL)", + [], + |row| row.get(0), + )?; + if pending { + return Err(UsdtError::PendingTransfer); + } + Ok(()) +} diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs new file mode 100644 index 0000000..df74c39 --- /dev/null +++ b/src/modules/usdt/tests.rs @@ -0,0 +1,1967 @@ +use super::*; + +#[test] +fn account_and_signatures_match_entrypoint_v8_reference() { + use alloy_primitives::{Address, Bytes, B256}; + let vector: serde_json::Value = + serde_json::from_str(include_str!("fixtures/eip7702-vectors.json")).unwrap(); + let address: Address = vector["address"].as_str().unwrap().parse().unwrap(); + assert_eq!( + usdt_address(TEST_PHRASE.into(), None).unwrap(), + address.to_checksum(None) + ); + let mut op: user_operation::UserOperation = + serde_json::from_value(vector["operation"].clone()).unwrap(); + let calls: Vec<(Address, Bytes)> = vector["calls"] + .as_array() + .unwrap() + .iter() + .map(|call| { + ( + call["to"].as_str().unwrap().parse().unwrap(), + call["data"].as_str().unwrap().parse().unwrap(), + ) + }) + .collect(); + assert_eq!(account::batch(&calls), op.call_data); + let expected_hash: B256 = vector["userOperationHash"] + .as_str() + .unwrap() + .parse() + .unwrap(); + assert_eq!(op.hash(42161).unwrap(), expected_hash); + assert_eq!( + op.eip7702_auth.hash().unwrap(), + vector["authorizationHash"] + .as_str() + .unwrap() + .parse::() + .unwrap() + ); + let key = keys::derive_key(TEST_PHRASE.into(), None).unwrap(); + assert_eq!(op.sign(&key, 42161).unwrap(), expected_hash); + assert_eq!( + op.signature, + vector["signature"] + .as_str() + .unwrap() + .parse::() + .unwrap() + ); + assert_eq!( + op.eip7702_auth, + serde_json::from_value(vector["operation"]["eip7702Auth"].clone()).unwrap() + ); + assert_ne!(op.hash(42161).unwrap(), op.hash(1).unwrap()); + op.sender = RECIPIENT.parse().unwrap(); + assert!(matches!( + op.sign(&key, 42161), + Err(UsdtError::InvalidCredentials) + )); +} + +#[test] +fn amount_round_trips_without_rounding() { + for (text, amount) in [ + ("0", 0), + ("0.000001", 1), + ("12.345678", 12_345_678), + ("18446744073709.551615", u64::MAX), + ] { + assert_eq!(usdt_parse_amount(text.into()).unwrap(), amount); + assert_eq!(usdt_format_amount(amount), text); + } + for invalid in [ + "-1", + "+1", + "1e6", + "1.0000001", + "18446744073709.551616", + "1,000", + ".", + "", + ] { + assert!(usdt_parse_amount(invalid.into()).is_err()); + } +} + +#[test] +fn wallet_requires_both_provider_endpoints() { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("usdt.sqlite"); + for (rpc, bundler) in [ + ("", "https://provider.example"), + ("https://provider.example", ""), + ] { + assert!(matches!( + UsdtWallet::new( + RECIPIENT.into(), + path.to_string_lossy().into(), + rpc.into(), + bundler.into() + ), + Err(UsdtError::NotConfigured) + )); + } + assert!(!path.exists()); +} + +#[test] +fn address_matches_standard_ethereum_recovery_path() { + let phrase = "test test test test test test test test test test test junk"; + assert_eq!( + usdt_address(phrase.into(), None).unwrap(), + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + ); + assert_ne!( + usdt_address(phrase.into(), Some("secret".into())).unwrap(), + usdt_address(phrase.into(), None).unwrap() + ); +} + +#[test] +fn payment_request_rejects_wrong_chain_and_malformed_checksum() { + let address = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; + assert_eq!( + usdt_parse_payment_request(format!("ethereum:{address}@42161")) + .unwrap() + .recipient, + address + ); + let request = format!( + "ethereum:{}@42161/transfer?address={address}", + types::TOKEN.to_checksum(None) + ); + assert_eq!( + usdt_parse_payment_request(request).unwrap().recipient, + address + ); + for invalid in [ + format!("ethereum:{address}@42161/transfer?address={address}"), + format!("ethereum:{address}@1"), + format!("ethereum:{address}@42161?value=1"), + "0x0000000000000000000000000000000000000000".into(), + "0xF39Fd6e51aad88F6F4ce6aB8827279cffFb92266".into(), + ] { + assert!(usdt_parse_payment_request(invalid).is_err()); + } +} + +#[test] +fn payment_requests_preserve_exact_token_amounts_and_reject_ambiguous_terms() { + let uri = |query: &str| { + format!( + "ethereum:pay-{}@42161/transfer?{query}", + types::TOKEN.to_checksum(None) + ) + }; + for (encoded, expected) in [ + ("1", 1), + ("1500000", 1_500_000), + ("1.5e6", 1_500_000), + (".5E6", 500_000), + ("+1.0000000e6", 1_000_000), + ("%2B1e6", 1_000_000), + ("0", 0), + (".0", 0), + ("18446744073709551615", u64::MAX), + ] { + let value = uri(&format!("uint256={encoded}&address={RECIPIENT}")); + let request = usdt_parse_payment_request(value).unwrap(); + assert_eq!(request.recipient, RECIPIENT); + assert_eq!(request.amount, Some(expected)); + } + for value in [RECIPIENT.into(), uri(&format!("address={RECIPIENT}"))] { + assert_eq!(usdt_parse_payment_request(value).unwrap().amount, None); + } + for amount in [ + "-1", + "1.01", + "1e-1", + "18446744073709551616", + "1e100000", + "1e", + "NaN", + "", + "1.2.3", + ] { + assert!( + usdt_parse_payment_request(uri(&format!("address={RECIPIENT}&uint256={amount}"))) + .is_err() + ); + } + for query in [ + format!("address={RECIPIENT}&address={RECIPIENT}"), + format!("address={RECIPIENT}&uint256=1&uint256=2"), + format!("address={RECIPIENT}&value=1"), + format!("address={RECIPIENT}&data=0x00"), + "uint256=1000000".into(), + ] { + assert!(usdt_parse_payment_request(uri(&query)).is_err()); + } + assert!(usdt_parse_payment_request( + uri(&format!("address={RECIPIENT}&uint256=1")).replace("@42161", "@1") + ) + .is_err()); +} + +const TEST_PHRASE: &str = "test test test test test test test test test test test junk"; +const RECIPIENT: &str = "0x1111111111111111111111111111111111111111"; + +struct MockChain { + url: String, + state: std::sync::Arc>, + task: tokio::task::JoinHandle<()>, +} +struct ChainState { + chain: u64, + account_code: alloy_primitives::Bytes, + authorization_nonce: u64, + paymaster_valid_until: Option, + pre_verification_estimates: std::collections::VecDeque, + external_outgoing: bool, + nonce: u64, + balance: alloy_primitives::U256, + operations: Vec, + mined: bool, + tip: u64, + timestamp: alloy_primitives::U256, + reject_broadcast: bool, + delay_gas_estimate: bool, + paymaster: alloy_primitives::Address, + helper_balance: alloy_primitives::U256, + history_input: Option, + history_target: Option, + receipt_logs: Option>, + incoming: bool, + hide_logs: bool, + replacement_block: Option, + log_error: Option<(i64, String)>, + max_log_range: Option, + oversized_logs: bool, + log_requests: usize, + incoming_count: u64, + block_reads: usize, + fail_block_read_at: Option, +} +impl Drop for MockChain { + fn drop(&mut self) { + self.task.abort(); + } +} +impl MockChain { + async fn start() -> Self { + use alloy_primitives::U256; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let url = format!("http://{}", listener.local_addr().unwrap()); + let state = std::sync::Arc::new(std::sync::Mutex::new(ChainState { + chain: 42161, + account_code: alloy_primitives::Bytes::new(), + authorization_nonce: 0, + paymaster_valid_until: None, + pre_verification_estimates: Default::default(), + external_outgoing: false, + nonce: 0, + balance: U256::from(10_000_000), + operations: vec![], + mined: false, + tip: 20000, + timestamp: U256::from(wallet::now()), + reject_broadcast: false, + delay_gas_estimate: false, + paymaster: paymaster::PAYMASTER, + helper_balance: U256::from(1_000_000_000_000_000u64), + history_input: None, + history_target: Some(account::ENTRY_POINT), + receipt_logs: None, + incoming: false, + hide_logs: false, + replacement_block: None, + log_error: None, + max_log_range: None, + oversized_logs: false, + log_requests: 0, + incoming_count: 0, + block_reads: 0, + fail_block_read_at: None, + })); + let server_state = state.clone(); + let task = tokio::spawn(async move { + while let Ok((mut socket, _)) = listener.accept().await { + let mut request = Vec::new(); + let header_end = loop { + let mut chunk = [0u8; 4096]; + let n = socket.read(&mut chunk).await.unwrap(); + if n == 0 { + break 0; + } + request.extend_from_slice(&chunk[..n]); + if let Some(p) = request.windows(4).position(|w| w == b"\r\n\r\n") { + break p + 4; + } + }; + if header_end == 0 { + continue; + } + let length: usize = String::from_utf8_lossy(&request[..header_end]) + .lines() + .find_map(|line| { + line.to_lowercase() + .strip_prefix("content-length:") + .map(|v| v.trim().parse().unwrap()) + }) + .unwrap(); + while request.len() < header_end + length { + let mut chunk = [0u8; 4096]; + let n = socket.read(&mut chunk).await.unwrap(); + if n == 0 { + break; + } + request.extend_from_slice(&chunk[..n]); + } + let body: serde_json::Value = + serde_json::from_slice(&request[header_end..]).unwrap(); + let delay = body["method"] == "eth_estimateUserOperationGas" + && std::mem::take(&mut server_state.lock().unwrap().delay_gas_estimate); + if delay { + tokio::time::sleep(std::time::Duration::from_secs(6)).await; + } + let response = server_state.lock().unwrap().respond(&body).to_string(); + let response=format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{response}",response.len()); + let _ = socket.write_all(response.as_bytes()).await; + } + }); + Self { url, state, task } + } + fn wallet(&self, dir: &tempfile::TempDir) -> std::sync::Arc { + UsdtWallet::new( + usdt_address(TEST_PHRASE.into(), None).unwrap(), + dir.path().join("usdt.sqlite").to_string_lossy().into(), + self.url.clone(), + self.url.clone(), + ) + .unwrap() + } +} +impl ChainState { + fn respond(&mut self, body: &serde_json::Value) -> serde_json::Value { + use alloy_primitives::{Bytes, U256}; + use alloy_sol_types::{SolCall, SolValue}; + use serde_json::json; + if matches!( + body["method"].as_str(), + Some("eth_estimateUserOperationGas" | "eth_sendUserOperation") + ) { + let op = &body["params"][0]; + let auth = &op["eip7702Auth"]; + // Alto requires authorization with the marker, even for an existing delegation. + if op["factory"] != "0x7702" + || auth.is_null() + || serde_json::from_value::(auth["nonce"].clone()).ok() + != Some(U256::from(self.authorization_nonce)) + { + return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid EIP-7702 authorization"}}); + } + } + let result = match body["method"].as_str().unwrap() { + "eth_chainId" => json!(U256::from(self.chain)), + "eth_blockNumber" => json!(U256::from(self.tip)), + "eth_getBlockByNumber" => { + self.block_reads += 1; + if self.fail_block_read_at == Some(self.block_reads) { + self.fail_block_read_at = None; + return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"temporarily unavailable"}}); + } + json!({"timestamp":self.timestamp}) + } + "eth_getCode" => json!(self.account_code), + "eth_getTransactionCount" => json!(U256::from(self.authorization_nonce)), + "eth_getBalance" => json!(self.helper_balance), + "eth_call" => { + let data: Bytes = + serde_json::from_value(body["params"][0]["data"].clone()).unwrap(); + use transaction::{BridgeHelper, MessagingFee, OFTLimit, OFTReceipt, Oft}; + let encoded = if data.starts_with(&transaction::EntryPoint::getNonceCall::SELECTOR) + { + let block = serde_json::from_value::(body["params"][1].clone()).ok(); + let nonce = if self + .replacement_block + .zip(block) + .is_some_and(|(mined, at)| at < U256::from(mined)) + { + 0 + } else { + self.nonce + }; + U256::from(nonce).abi_encode() + } else if data.starts_with(&Oft::tokenCall::SELECTOR) { + types::TOKEN.abi_encode() + } else if data.starts_with(&Oft::peersCall::SELECTOR) { + alloy_primitives::B256::repeat_byte(1).abi_encode() + } else if data.starts_with(&Oft::quoteOFTCall::SELECTOR) { + let param = Oft::quoteOFTCall::abi_decode(&data).unwrap().param; + ( + OFTLimit { + minAmountLD: U256::from(1), + maxAmountLD: U256::MAX, + }, + Vec::::new(), + OFTReceipt { + amountSentLD: param.amountLD, + amountReceivedLD: param.amountLD, + }, + ) + .abi_encode_params() + } else if data.starts_with(&Oft::quoteSendCall::SELECTOR) { + MessagingFee { + nativeFee: U256::from(10_000_000_000u64), + lzTokenFee: U256::ZERO, + } + .abi_encode() + } else if data.starts_with(&BridgeHelper::maxGasCall::SELECTOR) { + U256::from(1_000_000_000_000_000u64).abi_encode() + } else if data.starts_with(&BridgeHelper::quoteSendCall::SELECTOR) { + let param = BridgeHelper::quoteSendCall::abi_decode(&data) + .unwrap() + .param; + (param.amountLD + U256::from(300_000)).abi_encode() + } else { + self.balance.abi_encode() + }; + json!(Bytes::from(encoded)) + } + "pimlico_getTokenQuotes" => { + json!({"quotes":[{"token":types::TOKEN,"paymaster":self.paymaster,"postOpGas":"0xc350","exchangeRate":U256::from(3_000_000_000u64)}]}) + } + "pimlico_getUserOperationGasPrice" => { + json!({"fast":{"maxFeePerGas":U256::from(50_000_000),"maxPriorityFeePerGas":U256::from(100000)}}) + } + "pm_getPaymasterData" | "pm_getPaymasterStubData" => { + let mut data = vec![0; 118]; + data[0] = 3; + if body["method"] == "pm_getPaymasterData" { + data[2..8].copy_from_slice( + &self + .paymaster_valid_until + .unwrap_or(u64::try_from(self.timestamp).unwrap() + 600) + .to_be_bytes()[2..], + ); + } + data[14..34].copy_from_slice(types::TOKEN.as_slice()); + data[34..50].copy_from_slice(&50_000u128.to_be_bytes()); + data[50..82].copy_from_slice(&U256::from(3_000_000_000u64).to_be_bytes::<32>()); + data.extend_from_slice(&[1; 65]); + let mut response = + json!({"paymaster":self.paymaster,"paymasterData":Bytes::from(data)}); + if body["method"] == "pm_getPaymasterStubData" { + response["paymasterPostOpGasLimit"] = json!("0x186a0"); + } + response + } + "eth_estimateUserOperationGas" => { + let data: Bytes = + serde_json::from_value(body["params"][0]["paymasterData"].clone()).unwrap(); + let verification_gas = if data[2..8].iter().any(|byte| *byte != 0) { + "0x13880" + } else { + "0xc350" + }; + let pre_verification_gas = self + .pre_verification_estimates + .pop_front() + .unwrap_or(80_000); + json!({"callGasLimit":"0x249f0","verificationGasLimit":"0x3d090","preVerificationGas":U256::from(pre_verification_gas),"paymasterVerificationGasLimit":verification_gas,"paymasterPostOpGasLimit":"0xc350"}) + } + "eth_sendUserOperation" => { + let op: user_operation::UserOperation = + serde_json::from_value(body["params"][0].clone()).unwrap(); + let hash = op.hash(types::CHAIN_ID).unwrap(); + self.operations.push(op); + if self.reject_broadcast { + return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"temporarily unavailable"}}); + } + json!(hash) + } + "eth_getLogs" => { + self.log_requests += 1; + if let Some((code, message)) = &self.log_error { + return json!({"jsonrpc":"2.0","id":1,"error":{"code":code,"message":message}}); + } + let filter = &body["params"][0]; + let from: U256 = serde_json::from_value(filter["fromBlock"].clone()).unwrap(); + let to: U256 = serde_json::from_value(filter["toBlock"].clone()).unwrap(); + if self + .max_log_range + .is_some_and(|limit| to - from + U256::from(1) > U256::from(limit)) + { + if self.oversized_logs { + return json!({"jsonrpc":"2.0","id":1,"result":[],"padding":" ".repeat(2_097_152)}); + } + return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32005,"message":"Log query limit exceeded"}}); + } + if let Some(block) = self.replacement_block { + if !self.hide_logs + && from <= U256::from(block) + && to >= U256::from(block) + && (filter["address"] == json!(account::ENTRY_POINT) + || filter["address"].is_array()) + && filter["topics"][1].is_null() + { + use alloy_sol_types::SolEvent; + let event = transaction::EntryPoint::UserOperationEvent { + userOpHash: alloy_primitives::B256::repeat_byte(0xbb), + sender: self.operations[0].sender, + paymaster: self.paymaster, + nonce: U256::ZERO, + success: true, + actualGasCost: U256::from(1), + actualGasUsed: U256::from(1), + } + .encode_log_data(); + return json!({"jsonrpc":"2.0","id":1,"result":[{"address":account::ENTRY_POINT,"topics":event.topics(),"data":event.data,"transactionHash":alloy_primitives::B256::repeat_byte(8),"blockNumber":U256::from(block),"logIndex":"0x1"}]}); + } + } + if self.external_outgoing + && filter["address"] == json!(types::TOKEN) + && !filter["topics"][1].is_null() + && from <= U256::from(20000) + && to >= U256::from(20000) + { + return json!({"jsonrpc":"2.0","id":1,"result":[self.event_logs().last().unwrap()]}); + } + if self.incoming_count > 0 + && (filter["address"] == json!(types::TOKEN) || filter["address"].is_array()) + && !filter["topics"][2].is_null() + { + use alloy_sol_types::SolEvent; + let event = transaction::Erc20::Transfer { + from: alloy_primitives::Address::repeat_byte(8), + to: self.operations[0].sender, + value: U256::from(42), + } + .encode_log_data(); + let logs: Vec<_> = (1..=self.incoming_count).filter(|index| U256::from(20000 + index) >= from && U256::from(20000 + index) <= to).map(|index| { + json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"transactionHash":format!("0x{index:064x}"),"blockNumber":U256::from(20000+index),"logIndex":"0x0"}) + }).collect(); + return json!({"jsonrpc":"2.0","id":1,"result":logs}); + } + if self.mined + && !self.hide_logs + && from <= U256::from(20000) + && to >= U256::from(20000) + && (filter["address"] == json!(account::ENTRY_POINT) + || filter["address"].is_array()) + { + json!([self.event_logs()[1]]) + } else { + json!([]) + } + } + "eth_getTransactionReceipt" => { + json!({"logs":self.receipt_logs.clone().unwrap_or_else(|| self.event_logs())}) + } + "eth_getTransactionByHash" => { + let op = &self.operations[0]; + let input = transaction::EntryPoint::handleOpsCall { + ops: vec![transaction::PackedOperation { + sender: op.sender, + nonce: op.nonce, + // EntryPoint's onchain marker is right-padded to 20 bytes. + initCode: Bytes::from_static(&alloy_primitives::hex!( + "7702000000000000000000000000000000000000" + )), + callData: op.call_data.clone(), + accountGasLimits: Default::default(), + preVerificationGas: op.pre_verification_gas, + gasFees: Default::default(), + paymasterAndData: op.paymaster_and_data().unwrap(), + signature: op.signature.clone(), + }], + beneficiary: alloy_primitives::Address::repeat_byte(9), + } + .abi_encode(); + json!({"to":self.history_target,"input":self.history_input.clone().unwrap_or_else(|| Bytes::from(input))}) + } + method => panic!("Unexpected RPC method {method}"), + }; + json!({"jsonrpc":"2.0","id":1,"result":result}) + } + fn event_logs(&self) -> Vec { + use alloy_primitives::{B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let op = self.operations.first().unwrap(); + let hash = op.hash(types::CHAIN_ID).unwrap(); + let gas = transaction::Paymaster::UserOperationSponsored { + userOpHash: hash, + user: op.sender, + paymasterMode: 1, + token: types::TOKEN, + tokenAmountPaid: U256::from(123), + exchangeRate: U256::from(3_000_000_000u64), + } + .encode_log_data(); + let event = transaction::EntryPoint::UserOperationEvent { + userOpHash: hash, + sender: op.sender, + paymaster: paymaster::PAYMASTER, + nonce: op.nonce, + success: true, + actualGasCost: U256::from(1), + actualGasUsed: U256::from(1), + } + .encode_log_data(); + let mut logs = vec![ + json!({"address":paymaster::PAYMASTER,"topics":gas.topics(),"data":gas.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x0"}), + json!({"address":account::ENTRY_POINT.to_checksum(None),"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x1"}), + ]; + if !self.mined { + logs.clear(); + } + if self.incoming { + let event = transaction::Erc20::Transfer { + from: alloy_primitives::Address::repeat_byte(8), + to: op.sender, + value: U256::from(42), + } + .encode_log_data(); + logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"logIndex":"0x2"})); + } + if self.external_outgoing { + let event = transaction::Erc20::Transfer { + from: op.sender, + to: RECIPIENT.parse().unwrap(), + value: U256::from(77), + } + .encode_log_data(); + logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x3"})); + } + logs + } +} + +#[tokio::test] +async fn signed_operation_survives_uncertain_broadcast_and_restart() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = tokio::time::timeout( + std::time::Duration::from_secs(3), + wallet.quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum), + ) + .await + .expect("An idle wallet must quote without a fixed per-request delay") + .unwrap(); + let next = wallet + .quote_transfer(RECIPIENT.into(), 2_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().reject_broadcast = true; + let sent = wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await + .unwrap(); + assert_eq!(sent.status, UsdtTransferStatus::Pending); + assert!(sent.tx_hash.is_empty()); + let op = chain.state.lock().unwrap().operations[0].clone(); + assert_eq!(op.sender.to_checksum(None), wallet.receive_address()); + assert_eq!( + op.paymaster_verification_gas_limit, + alloy_primitives::U256::from(80_000) + ); + assert_eq!( + op.paymaster_post_op_gas_limit, + alloy_primitives::U256::from(100_000) + ); + drop(wallet); + let wallet = chain.wallet(&dir); + assert_eq!( + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap() + .user_operation_hash, + sent.user_operation_hash + ); + // A pending payment is rejected locally even if the provider is now misconfigured. + chain.state.lock().unwrap().chain = 1; + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 2_000_000, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::PendingTransfer) + )); + assert!(matches!( + wallet.send(next.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::PendingTransfer) + )); + chain.state.lock().unwrap().chain = 42161; + chain.state.lock().unwrap().tip += 3; + wallet.refresh_transfers().await.unwrap(); + assert!(chain + .state + .lock() + .unwrap() + .operations + .iter() + .all(|item| serde_json::to_value(item).unwrap() == serde_json::to_value(&op).unwrap())); + chain.state.lock().unwrap().mined = true; + let history = wallet.refresh_transfers().await.unwrap(); + assert_eq!(history.len(), 1); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); + assert!(!history[0].tx_hash.is_empty()); +} + +#[tokio::test] +async fn fluctuating_gas_estimates_preserve_the_reviewed_operation() { + use alloy_primitives::U256; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + chain.state.lock().unwrap().pre_verification_estimates = + [80_000, 80_100, 80_200, 80_300].into(); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let reviewed = wallet.store.quote("e.id).unwrap().plan.operation; + chain.state.lock().unwrap().pre_verification_estimates = [100_000].into(); + assert!(matches!( + wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await, + Err(UsdtError::QuoteExpired) + )); + assert!(wallet.history().unwrap().is_empty()); + assert!(chain.state.lock().unwrap().operations.is_empty()); + + chain.state.lock().unwrap().pre_verification_estimates = [80_400].into(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let signed = chain.state.lock().unwrap().operations[0].clone(); + assert_eq!( + signed.hash(types::CHAIN_ID).unwrap(), + reviewed.hash(types::CHAIN_ID).unwrap() + ); + assert!(signed.pre_verification_gas >= U256::from(80_400)); + assert!(signed.pre_verification_gas < U256::from(100_000)); +} + +#[tokio::test] +async fn wrong_network_owner_nonce_balance_and_paymaster_cannot_sign() { + use alloy_primitives::{Address, U256}; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + for amount in ["", "&uint256=2000000"] { + let request = format!( + "ethereum:{}@42161/transfer?address={RECIPIENT}{amount}", + types::TOKEN.to_checksum(None) + ); + assert!(matches!( + wallet + .quote_transfer(request, 1_000_000, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::InvalidAddress) + )); + } + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + assert!(matches!( + wallet + .send(quote.id.clone(), TEST_PHRASE.into(), Some("wrong".into())) + .await, + Err(UsdtError::InvalidCredentials) + )); + chain.state.lock().unwrap().chain = 1; + assert!(matches!( + wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await, + Err(UsdtError::WrongNetwork) + )); + { + let mut state = chain.state.lock().unwrap(); + state.chain = 42161; + state.nonce = 1; + } + assert!(matches!( + wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await, + Err(UsdtError::QuoteExpired) + )); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 0; + state.balance = U256::ZERO; + } + assert!(matches!( + wallet.send(quote.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::InsufficientBalance) + )); + { + let mut state = chain.state.lock().unwrap(); + state.balance = U256::from(10_000_000); + state.paymaster = Address::repeat_byte(1); + } + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::UnsupportedRoute) + )); + assert!(chain.state.lock().unwrap().operations.is_empty()); +} + +#[tokio::test] +async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.timestamp += alloy_primitives::U256::from(180); + state.tip += 3; + state.max_log_range = Some(1); + } + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::PendingTransfer) + )); + chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(421); + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Failed + ); + let next = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + assert_eq!( + wallet + .send(next.id, TEST_PHRASE.into(), None) + .await + .unwrap() + .status, + UsdtTransferStatus::Pending + ); +} + +#[tokio::test] +async fn bridge_payment_bounds_token_fees_and_revokes_helper_approval() { + use alloy_primitives::U256; + use alloy_sol_types::SolCall; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .await + .unwrap(); + let plan = wallet.store.quote("e.id).unwrap().plan; + let calls = history::decode_calls(&plan.operation.call_data).unwrap(); + assert_eq!(calls.len(), 4); + let paymaster = transaction::Erc20::approveCall::abi_decode(&calls[0].1).unwrap(); + assert_eq!(calls[0].0, types::TOKEN); + assert_eq!(paymaster.spender, paymaster::PAYMASTER); + assert!(paymaster.amount > U256::from(quote.maximum_fee - plan.bridge_fee)); + let approval = transaction::Erc20::approveCall::abi_decode(&calls[1].1).unwrap(); + assert_eq!(calls[1].0, types::TOKEN); + assert_eq!(approval.spender, types::BRIDGE_HELPER); + assert_eq!(approval.amount, U256::from(1_360_001)); + let send = transaction::BridgeHelper::sendCall::abi_decode(&calls[2].1).unwrap(); + assert_eq!(calls[2].0, types::BRIDGE_HELPER); + assert_eq!(send.oft, types::OFT); + assert_eq!(send.param.dstEid, 30109); + assert_eq!( + send.param.to, + RECIPIENT + .parse::() + .unwrap() + .into_word() + ); + assert_eq!(send.param.minAmountLD, U256::from(1_000_000)); + assert_eq!(send.fee.nativeFee, U256::from(10_000_000_000u64)); + let revoke = transaction::Erc20::approveCall::abi_decode(&calls[3].1).unwrap(); + assert_eq!(revoke.spender, types::BRIDGE_HELPER); + assert!(revoke.amount.is_zero()); + assert_eq!(plan.bridge_fee, 360_001); + assert_eq!(quote.received_amount, 1_000_000); + chain.state.lock().unwrap().helper_balance = U256::ZERO; + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .await, + Err(UsdtError::UnsupportedRoute) + )); +} + +#[tokio::test] +async fn seed_restore_recovers_mined_payments_without_local_submission_data() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + assert_eq!(restored.receive_address(), wallet.receive_address()); + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 1); + assert_eq!(history[0].user_operation_hash, sent.user_operation_hash); + assert_eq!(history[0].recipient, RECIPIENT); + assert_eq!(history[0].amount, 1_000_000); + assert_eq!(history[0].fee, Some(123)); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), 1); +} + +#[tokio::test] +async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_fee() { + use alloy_primitives::{B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .await + .unwrap(); + let mut transfer = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let own_hash = transfer + .user_operation_hash + .as_ref() + .unwrap() + .parse() + .unwrap(); + let other_hash = B256::repeat_byte(2); + let guid = B256::repeat_byte(3); + let event = |hash| { + transaction::EntryPoint::UserOperationEvent { + userOpHash: hash, + sender: wallet.address, + paymaster: paymaster::PAYMASTER, + nonce: U256::ZERO, + success: true, + actualGasCost: U256::from(1), + actualGasUsed: U256::from(1), + } + .encode_log_data() + }; + let log = |address, data: alloy_primitives::LogData| json!({"address":address,"topics":data.topics(),"data":data.data}); + let receipt = json!({"logs":[ + log(types::OFT, transaction::Oft::OFTSent { guid, dstEid:30109, fromAddress:types::BRIDGE_HELPER, amountSentLD:U256::from(1_000_000), amountReceivedLD:U256::from(1_000_000) }.encode_log_data()), + log(types::BRIDGE_HELPER, transaction::BridgeHelper::LogSend { sender:wallet.address, oft:types::OFT, amountLD:U256::from(1_000_000), nativeFee:U256::from(100), feeInToken:U256::from(500), totalAmount:U256::from(1_000_500) }.encode_log_data()), + log(account::ENTRY_POINT, event(other_hash)), + log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:own_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(123), exchangeRate:U256::from(1) }.encode_log_data()), + log(account::ENTRY_POINT, event(own_hash)), + ]}); + wallet.settle(&mut transfer, &receipt, false).unwrap(); + assert_eq!(transfer.status, UsdtTransferStatus::Failed); + assert_eq!(transfer.fee, Some(123)); + assert_eq!(transfer.bridge_fee, 0); + assert_eq!(transfer.bridge_guid, None); + wallet.settle(&mut transfer, &receipt, true).unwrap(); + assert_eq!(transfer.status, UsdtTransferStatus::BridgeNeedsAttention); + assert_eq!(transfer.bridge_guid, None); +} + +#[tokio::test] +#[ignore = "requires a fresh local Arbitrum fork and tests/usdt-fork/provider.mjs"] +async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically() { + use alloy_primitives::U256; + use serde_json::json; + let rpc = rpc::Rpc::new("http://127.0.0.1:18545".into(), types::CHAIN_ID).unwrap(); + let client: String = rpc.call("web3_clientVersion", json!([])).await.unwrap(); + assert!(client.to_lowercase().contains("anvil")); + let dir = tempfile::tempdir().unwrap(); + let wallet = UsdtWallet::new( + usdt_address(TEST_PHRASE.into(), None).unwrap(), + dir.path().join("usdt.sqlite").to_string_lossy().into(), + std::env::var("USDT_FORK_RPC_URL").unwrap_or_else(|_| "http://127.0.0.1:18545".into()), + std::env::var("USDT_FORK_BUNDLER_URL").unwrap_or_else(|_| "http://127.0.0.1:18546".into()), + ) + .unwrap(); + assert_eq!(rpc.balance(wallet.address).await.unwrap(), U256::ZERO); + let initial = wallet.balance().await.unwrap(); + // Only locally mined transactions belong to this fixture's history. + wallet + .store + .complete_history(wallet.block_number().await.unwrap()) + .unwrap(); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let history = wallet.refresh_transfers().await.unwrap(); + let transfer = history.iter().find(|t| t.id == sent.id).unwrap(); + assert_eq!(transfer.status, UsdtTransferStatus::Confirmed); + let fee = transfer.fee.unwrap(); + assert!(fee > 0 && fee <= quote.maximum_fee); + assert_eq!(wallet.balance().await.unwrap(), initial - 1_000_000 - fee); + + let bridge = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .await + .unwrap(); + let helper_balance = rpc.balance(types::BRIDGE_HELPER).await.unwrap(); + let _: serde_json::Value = rpc + .call("anvil_setBalance", json!([types::BRIDGE_HELPER, "0x0"])) + .await + .unwrap(); + let before = wallet.balance().await.unwrap(); + let sent = wallet + .send(bridge.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let history = wallet.refresh_transfers().await.unwrap(); + let failed = history.iter().find(|t| t.id == sent.id).unwrap(); + assert_eq!(failed.status, UsdtTransferStatus::Failed); + assert_eq!(failed.bridge_fee, 0); + assert!(failed.bridge_guid.is_none()); + assert!(before - wallet.balance().await.unwrap() <= bridge.maximum_fee); + let _: serde_json::Value = rpc + .call( + "anvil_setBalance", + json!([types::BRIDGE_HELPER, helper_balance]), + ) + .await + .unwrap(); + + let bridge = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .await + .unwrap(); + let before = wallet.balance().await.unwrap(); + let sent = wallet + .send(bridge.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let history = wallet.refresh_transfers().await.unwrap(); + let bridged = history.iter().find(|t| t.id == sent.id).unwrap(); + assert_eq!(bridged.status, UsdtTransferStatus::Bridging); + assert!(bridged.bridge_guid.is_some()); + assert!(bridged.bridge_fee > 0); + assert!(bridged.fee.unwrap() <= bridge.maximum_fee); + sync_history_to_tip(&wallet).await; + assert_eq!( + wallet.balance().await.unwrap(), + before - 1_000_000 - bridged.fee.unwrap() + ); + alloy_sol_types::sol! { function allowance(address owner, address spender) view returns (uint256); } + let allowance = rpc + .contract( + types::TOKEN, + allowanceCall { + owner: wallet.address, + spender: types::BRIDGE_HELPER, + }, + ) + .await + .unwrap(); + assert!(allowance.is_zero()); + assert_eq!(rpc.balance(wallet.address).await.unwrap(), U256::ZERO); +} + +#[tokio::test] +async fn history_preserves_receipts_with_external_account_call_shapes() { + use alloy_primitives::{Bytes, U256}; + use alloy_sol_types::SolCall; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let original = chain.state.lock().unwrap().operations[0].call_data.clone(); + let single = account::SimpleAccount::executeCall { + target: types::TOKEN, + value: U256::ZERO, + data: transaction::Erc20::transferCall { + recipient: RECIPIENT.parse().unwrap(), + amount: U256::from(1_000_000), + } + .abi_encode() + .into(), + } + .abi_encode() + .into(); + for (call_data, outer, expected_outgoing) in [ + (original.clone(), None, true), + (single, None, true), + (Bytes::from_static(&[1, 2, 3, 4]), None, false), + (original, Some(Bytes::from_static(&[5, 6, 7, 8])), false), + ] { + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.incoming = true; + state.tip = 20003; + state.operations[0].call_data = call_data; + state.history_input = outer; + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!( + history + .iter() + .filter(|t| t.is_incoming && t.amount == 42) + .count(), + 1 + ); + assert_eq!( + history.iter().filter(|t| !t.is_incoming).count(), + usize::from(expected_outgoing) + ); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), history.len()); + } +} + +#[tokio::test] +async fn wrapped_history_preserves_signed_payments_and_restores_token_transfers() { + use alloy_primitives::{Address, Bytes, U256}; + use alloy_sol_types::{SolCall, SolEvent}; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let decoy_input = { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + let tx = state.respond(&json!({"method":"eth_getTransactionByHash"})); + let input: Bytes = serde_json::from_value(tx["result"]["input"].clone()).unwrap(); + let mut batch = transaction::EntryPoint::handleOpsCall::abi_decode(&input).unwrap(); + batch.ops[0].callData = account::batch(&[( + types::TOKEN, + transaction::Erc20::transferCall { + recipient: Address::repeat_byte(0x22), + amount: U256::from(9_000_000), + } + .abi_encode() + .into(), + )]); + let mut logs = state.event_logs(); + for (recipient, amount) in [ + (RECIPIENT.parse().unwrap(), 1_000_000), + (paymaster::PAYMASTER, 123), + ] { + let transfer = transaction::Erc20::Transfer { + from: wallet.address, + to: recipient, + value: U256::from(amount), + } + .encode_log_data(); + logs.insert( + 0, + json!({"address":types::TOKEN,"topics":transfer.topics(),"data":transfer.data}), + ); + } + for (index, log) in logs.iter_mut().enumerate() { + log["logIndex"] = json!(U256::from(index)); + } + state.receipt_logs = Some(logs); + state.history_target = Some(Address::repeat_byte(0x33)); + Bytes::from(batch.abi_encode()) + }; + drop(wallet); + for input in [decoy_input, Bytes::from_static(&[1, 2, 3, 4])] { + chain.state.lock().unwrap().history_input = Some(input); + let reopened = chain.wallet(&dir); + sync_history_to_tip(&reopened).await; + let history = reopened.history().unwrap(); + assert_eq!(history.len(), 1); + let payment = &history[0]; + assert_eq!(payment.id, sent.id); + assert_eq!(payment.user_operation_hash, sent.user_operation_hash); + assert_eq!(payment.amount, 1_000_000); + assert_eq!(payment.received_amount, 1_000_000); + assert_eq!(payment.recipient, RECIPIENT); + assert_eq!(payment.status, UsdtTransferStatus::Confirmed); + assert_eq!(payment.fee, Some(123)); + assert!(!payment.tx_hash.is_empty()); + assert!(reopened.store.pending_plan(&sent.id).unwrap().is_none()); + + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + for _ in 0..2 { + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 2); + for (recipient, amount) in [ + (RECIPIENT.into(), 1_000_000), + (paymaster::PAYMASTER.to_checksum(None), 123), + ] { + let transfer = history.iter().find(|t| t.recipient == recipient).unwrap(); + assert_eq!(transfer.amount, amount); + assert_eq!(transfer.user_operation_hash, None); + assert_eq!(transfer.status, UsdtTransferStatus::Confirmed); + assert!(!transfer.is_incoming); + } + } + } +} + +#[test] +fn stored_activity_is_complete_and_sorted_newest_first() { + let dir = tempfile::tempdir().unwrap(); + let store = store::Store::open( + dir.path().join("history.sqlite").to_str().unwrap(), + "account", + ) + .unwrap(); + let transfers: Vec<_> = (0..501) + .map(|index| UsdtTransfer { + id: format!("receipt-{index}"), + tx_hash: format!("tx-{index}"), + user_operation_hash: None, + bridge_guid: None, + recipient: RECIPIENT.into(), + destination: UsdtDestination::Arbitrum, + amount: 1, + received_amount: 1, + bridge_fee: 0, + fee: None, + is_incoming: true, + status: UsdtTransferStatus::Confirmed, + timestamp: index, + explorer_url: String::new(), + }) + .collect(); + store.save_history_receipt(&transfers, "receipt").unwrap(); + store.complete_history(1000).unwrap(); + let history = store.transfers().unwrap(); + assert_eq!(history.len(), 501); + assert_eq!(history.first().unwrap().id, "receipt-500"); + assert_eq!(history.last().unwrap().id, "receipt-0"); +} + +#[tokio::test] +async fn nonce_advance_with_delayed_logs_remains_pending_and_history_reconciles() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + for (recipient, destination) in [ + (wallet.receive_address(), UsdtDestination::Arbitrum), + (wallet.receive_address(), UsdtDestination::Ethereum), + (types::TOKEN.to_checksum(None), UsdtDestination::Arbitrum), + ] { + assert!(matches!( + wallet + .quote_transfer(recipient, 1_000_000, destination) + .await, + Err(UsdtError::InvalidAddress) + )); + } + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.hide_logs = true; + state.max_log_range = Some(1); + state.nonce = 1; + state.tip += 3; + state.timestamp += alloy_primitives::U256::from(180); + } + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::PendingTransfer) + )); + { + let mut state = chain.state.lock().unwrap(); + state.hide_logs = false; + state.max_log_range = None; + } + sync_history_to_tip(&wallet).await; + let history = wallet.history().unwrap(); + assert_eq!(history.len(), 1); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); +} + +#[tokio::test] +async fn interrupted_history_resumes_without_repeating_completed_work() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.incoming_count = 100; + state.tip = 508_000_000; + state.block_reads = 0; + state.fail_block_read_at = Some(25); + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + restored.store.save_history_progress(20_000).unwrap(); + let error = tokio::time::timeout(std::time::Duration::from_secs(90), async { + loop { + match restored.sync_history().await { + Ok(false) => {} + Ok(true) => panic!("The interrupted scan must report its provider error"), + Err(error) => break error, + } + } + }) + .await + .unwrap(); + assert!(matches!(error, UsdtError::TransactionRejected { .. })); + assert_eq!(restored.history().unwrap().len(), 24); + assert!(restored.store.history_progress().unwrap().is_some()); + drop(restored); + let restored = chain.wallet(&restored_dir); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), 100); + assert_eq!(chain.state.lock().unwrap().block_reads, 101); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), 100); + assert_eq!(chain.state.lock().unwrap().block_reads, 101); + assert!(restored.store.history_progress().unwrap().is_none()); + assert_eq!(restored.store.synced_block().unwrap().unwrap(), 507_999_998); + assert!(!restored + .store + .has_history_receipt(&restored.history().unwrap()[0].tx_hash) + .unwrap()); +} + +#[tokio::test] +async fn replacement_after_expiry_recovers_pending_send_after_restart() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().reject_broadcast = true; + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + drop(wallet); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 1; + state.timestamp += alloy_primitives::U256::from(600); + state.tip = 508_000_000; + state.replacement_block = Some(507_000_000); + state.hide_logs = true; + state.max_log_range = Some(1); + } + let wallet = chain.wallet(&dir); + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + chain.state.lock().unwrap().hide_logs = false; + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Replaced + ); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + assert_eq!( + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap() + .status, + UsdtTransferStatus::Pending + ); +} + +#[tokio::test] +async fn history_distinguishes_rate_limits_from_log_range_limits() { + for (code, message) in [ + (-32005, "Rate limit exceeded"), + (-32016, "Provider throttled"), + (-32000, "Invalid request"), + ] { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + { + let mut state = chain.state.lock().unwrap(); + state.log_error = Some((code, message.into())); + } + let error = wallet.sync_history().await.unwrap_err(); + if code == -32000 { + assert!(matches!(error, UsdtError::TransactionRejected { .. })); + } else { + assert!(matches!(error, UsdtError::RateLimited)); + } + assert!((1..=2).contains(&chain.state.lock().unwrap().log_requests)); + } + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + chain.state.lock().unwrap().log_error = Some((-32005, "Log query limit exceeded".into())); + assert!(matches!( + wallet + .rpc + .call::( + "eth_getLogs", + serde_json::json!([{"fromBlock":"0x0","toBlock":"0x1"}]) + ) + .await, + Err(UsdtError::LogRangeTooLarge) + )); + { + let mut state = chain.state.lock().unwrap(); + state.log_error = None; + state.max_log_range = Some(10_000); + state.oversized_logs = true; + } + sync_history_to_tip(&wallet).await; + assert_eq!(wallet.store.synced_block().unwrap(), Some(19_998)); +} + +#[tokio::test] +async fn history_budget_returns_incomplete_and_resumes_to_tip() { + let chain = MockChain::start().await; + chain.state.lock().unwrap().tip = 508_000_000; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + assert!(!wallet.sync_history().await.unwrap()); + let next = wallet.store.history_progress().unwrap().unwrap(); + assert!(next > 0 && next < 508_000_000); + sync_history_to_tip(&wallet).await; + assert_eq!(wallet.store.synced_block().unwrap(), Some(507_999_998)); +} + +#[tokio::test] +async fn quote_expiring_during_validation_is_not_signed() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let mut data = wallet.store.quote("e.id).unwrap(); + data.quote.id = "expiring-quote".into(); + data.quote.expires_at = wallet::now() + 10; + data.plan.expires_at = data.quote.expires_at; + wallet.store.save_quote(&data).unwrap(); + chain.state.lock().unwrap().delay_gas_estimate = true; + assert!(matches!( + wallet.send(data.quote.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::QuoteExpired) + )); + assert!(!chain.state.lock().unwrap().delay_gas_estimate); + assert!(wallet.history().unwrap().is_empty()); + assert!(chain.state.lock().unwrap().operations.is_empty()); +} + +async fn sync_history_to_tip(wallet: &UsdtWallet) { + tokio::time::timeout(std::time::Duration::from_secs(180), async { + while !wallet.sync_history().await.unwrap() {} + }) + .await + .expect("History must make progress within successive bounded scans"); +} + +#[tokio::test] +async fn deposit_http_throttling_is_distinct_from_network_failure() { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let url = format!("http://{}", listener.local_addr().unwrap()); + let server = tokio::spawn(async move { + let (mut socket, _) = listener.accept().await.unwrap(); + let mut request = [0u8; 4096]; + assert!(socket.read(&mut request).await.unwrap() > 0); + socket + .write_all( + b"HTTP/1.1 429 Too Many Requests\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ) + .await + .unwrap(); + }); + let client = + UsdtDepositClient::new(usdt_address(TEST_PHRASE.into(), None).unwrap(), url).unwrap(); + assert!(matches!( + client.networks().await, + Err(UsdtError::RateLimited) + )); + server.await.unwrap(); +} + +#[tokio::test] +async fn invalid_chain_data_and_stored_json_have_distinct_errors() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let db = rusqlite::Connection::open(dir.path().join("usdt.sqlite")).unwrap(); + db.execute("INSERT INTO usdt_quotes VALUES ('invalid','{')", []) + .unwrap(); + assert!(matches!( + wallet.store.quote("invalid"), + Err(UsdtError::Storage { .. }) + )); + db.execute( + "INSERT INTO usdt_transfers (id,hash,raw,data) VALUES ('invalid','hash','{','{')", + [], + ) + .unwrap(); + assert!(matches!(wallet.history(), Err(UsdtError::Storage { .. }))); + assert!(matches!( + wallet.store.pending_plan("invalid"), + Err(UsdtError::Storage { .. }) + )); + chain.state.lock().unwrap().timestamp = alloy_primitives::U256::MAX; + assert!(matches!( + wallet.block_timestamp(1).await, + Err(UsdtError::InvalidResponse) + )); +} + +#[tokio::test] +async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending() { + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }; + use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + time::{Duration, Instant}, + }; + let chain = MockChain::start().await; + let directory = tempfile::tempdir().unwrap(); + let mut wallet = chain.wallet(&directory); + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let address = listener.local_addr().unwrap(); + let attempts = Arc::new(Mutex::new(Vec::new())); + let stalled = Arc::new(AtomicBool::new(true)); + let accepted = attempts.clone(); + let stalled_server = stalled.clone(); + let server = tokio::spawn(async move { + let mut requests = tokio::task::JoinSet::new(); + while let Ok((socket, _)) = listener.accept().await { + let accepted = accepted.clone(); + let stalled = stalled_server.load(Ordering::SeqCst); + requests.spawn(async move { + let mut reader = BufReader::new(socket); + let mut line = String::new(); + reader.read_line(&mut line).await.unwrap(); + let hash = line.split_whitespace().nth(1).unwrap().trim_start_matches('/').to_string(); + loop { + line.clear(); + if reader.read_line(&mut line).await.unwrap() == 0 || line == "\r\n" { break; } + } + accepted.lock().unwrap().push(hash.clone()); + tokio::time::sleep(if stalled { Duration::from_secs(25) } else { Duration::from_millis(1200) }).await; + let index = hash.strip_prefix("bridge-tx-").unwrap(); + let body = serde_json::json!({"data":[{ + "guid":format!("guid-{index}"), + "pathway":{"srcEid":30110,"dstEid":30109,"sender":{"address":types::OFT}}, + "source":{"tx":{"txHash":hash}},"status":{"name":"DELIVERED"} + }]}).to_string(); + let response = format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", body.len(), body); + let _ = reader.into_inner().write_all(response.as_bytes()).await; + }); + } + }); + Arc::get_mut(&mut wallet).unwrap().rpc.bridge_status_url = Some(format!("http://{address}")); + let bridges: Vec<_> = (0..5) + .map(|index| UsdtTransfer { + id: format!("bridge-{index}"), + tx_hash: format!("bridge-tx-{index}"), + user_operation_hash: None, + bridge_guid: Some(format!("guid-{index}")), + recipient: RECIPIENT.into(), + destination: UsdtDestination::Polygon, + amount: 1_000_000, + received_amount: 1_000_000, + bridge_fee: 10, + fee: Some(20), + is_incoming: false, + status: UsdtTransferStatus::Bridging, + timestamp: 1, + explorer_url: String::new(), + }) + .collect(); + wallet + .store + .save_history_receipt(&bridges, "bridges") + .unwrap(); + // A signed operation with no nonce consumption must still resolve once expired. + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().reject_broadcast = true; + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.tip += 3; + state.timestamp += alloy_primitives::U256::from(601); + } + let started = Instant::now(); + let history = tokio::time::timeout(Duration::from_secs(9), wallet.refresh_transfers()) + .await + .unwrap() + .unwrap(); + assert!(started.elapsed() < Duration::from_secs(9)); + assert_eq!(attempts.lock().unwrap().len(), 1); + assert_eq!( + history + .iter() + .find(|transfer| transfer.id == sent.id) + .unwrap() + .status, + UsdtTransferStatus::Failed + ); + assert_eq!( + history + .iter() + .filter(|transfer| transfer.status == UsdtTransferStatus::Bridging) + .count(), + 5 + ); + chain.state.lock().unwrap().reject_broadcast = false; + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + // Hold the refresh lock before send, while all bridge status connections stall again. + let refresh_wallet = wallet.clone(); + let refresh = tokio::spawn(async move { refresh_wallet.refresh_transfers().await }); + tokio::time::timeout(Duration::from_secs(3), async { + while attempts.lock().unwrap().len() < 2 { + tokio::time::sleep(Duration::from_millis(10)).await; + } + }) + .await + .unwrap(); + let result = tokio::time::timeout( + Duration::from_secs(9), + wallet.send(quote.id, TEST_PHRASE.into(), None), + ) + .await; + refresh.await.unwrap().unwrap(); + assert_eq!(result.unwrap().unwrap().status, UsdtTransferStatus::Pending); + { + let attempts = attempts.lock().unwrap(); + assert_ne!(attempts[0], attempts[1]); + } + // Healthy responses slower than an equal share of the budget must still settle. + stalled.store(false, Ordering::SeqCst); + for _ in 0..2 { + tokio::time::timeout(Duration::from_secs(9), wallet.refresh_transfers()) + .await + .unwrap() + .unwrap(); + } + let history = wallet.history().unwrap(); + assert!(bridges.iter().all(|bridge| history.iter().any( + |transfer| transfer.id == bridge.id && transfer.status == UsdtTransferStatus::Confirmed + ))); + server.abort(); +} + +#[tokio::test] +async fn each_payment_authorizes_the_current_nonce_after_delegation() { + use alloy_primitives::{Bytes, U256}; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.account_code = + Bytes::from([&[0xef, 0x01, 0x00][..], account::DELEGATE.as_slice()].concat()); + state.authorization_nonce = 1; + state.nonce = 1; + state.mined = true; + state.tip += 3; + } + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Confirmed + ); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let state = chain.state.lock().unwrap(); + assert_eq!(state.operations.len(), 2); + for (nonce, operation) in state.operations.iter().enumerate() { + assert_eq!(operation.eip7702_auth.nonce, U256::from(nonce)); + assert_eq!(operation.nonce, U256::from(nonce)); + } + assert_ne!(state.operations[0].signature, state.operations[1].signature); +} + +#[tokio::test] +async fn consumed_authorization_preserves_pending_payment_until_signed_expiry() { + use alloy_primitives::{Bytes, U256}; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let quote_data = wallet.store.quote("e.id).unwrap(); + let dummy = quote_data.plan.operation.eip7702_auth; + assert_eq!(dummy, user_operation::Authorization::dummy(0)); + assert!(quote_data.plan.expires_at > quote.expires_at); + chain.state.lock().unwrap().authorization_nonce = 1; + assert!(matches!( + wallet.send(quote.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::QuoteExpired) + )); + assert!(chain.state.lock().unwrap().operations.is_empty()); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().reject_broadcast = true; + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let signed = wallet + .store + .pending_plan(&sent.id) + .unwrap() + .unwrap() + .operation; + assert_eq!(signed.eip7702_auth.nonce, U256::from(1)); + assert_ne!(signed.eip7702_auth.r, dummy.r); + drop(wallet); + { + let mut state = chain.state.lock().unwrap(); + state.account_code = + Bytes::from([&[0xef, 0x01, 0x00][..], account::DELEGATE.as_slice()].concat()); + state.authorization_nonce = 2; + state.tip += 3; + } + let restored = chain.wallet(&dir); + assert_eq!( + restored.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert_eq!(chain.state.lock().unwrap().operations.len(), 1); + let retained = restored.store.pending_plan(&sent.id).unwrap().unwrap(); + assert_eq!(retained.operation.eip7702_auth, signed.eip7702_auth); + assert_eq!(retained.operation.signature, signed.signature); + assert!(matches!( + restored + .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::PendingTransfer) + )); + chain.state.lock().unwrap().timestamp = U256::from(retained.expires_at + 1); + let history = restored.refresh_transfers().await.unwrap(); + assert_eq!(history[0].status, UsdtTransferStatus::Failed); + assert_eq!(history[0].fee, Some(0)); + assert!(restored.store.pending_plan(&sent.id).unwrap().is_none()); + let quote = restored + .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) + .await + .unwrap(); + assert_eq!( + restored + .store + .quote("e.id) + .unwrap() + .plan + .operation + .eip7702_auth + .nonce, + U256::from(2) + ); +} + +#[tokio::test] +async fn foreign_delegation_and_unbounded_paymaster_terms_cannot_authorize_payments() { + use alloy_primitives::{Bytes, U256}; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + chain.state.lock().unwrap().account_code = Bytes::from_static(&[0xef, 0x01, 0x00, 1]); + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::UnsupportedDelegation) + )); + assert_eq!(wallet.balance().await.unwrap(), 10_000_000); + assert!(wallet.history().unwrap().is_empty()); + chain.state.lock().unwrap().account_code = Bytes::new(); + let timestamp = u64::try_from(chain.state.lock().unwrap().timestamp).unwrap(); + for expiry in [0, timestamp + 901] { + chain.state.lock().unwrap().paymaster_valid_until = Some(expiry); + assert!(matches!( + wallet + .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) + .await, + Err(UsdtError::InvalidResponse) + )); + } + chain.state.lock().unwrap().paymaster_valid_until = Some(timestamp + 900); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().account_code = Bytes::from( + [ + &[0xef, 0x01, 0x00][..], + alloy_primitives::Address::repeat_byte(4).as_slice(), + ] + .concat(), + ); + assert!(matches!( + wallet.send(quote.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::UnsupportedDelegation) + )); + assert!(chain.state.lock().unwrap().operations.is_empty()); + assert_eq!(chain.state.lock().unwrap().nonce, 0); + assert_eq!(chain.state.lock().unwrap().balance, U256::from(10_000_000)); +} + +#[tokio::test] +async fn seed_restore_includes_external_token_sends_without_duplicate_operation_entries() { + use alloy_primitives::Bytes; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 77, UsdtDestination::Arbitrum) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.external_outgoing = true; + state.history_input = Some(Bytes::from_static(&[1, 2, 3, 4])); + state.tip += 3; + } + for unsupported_batch in [false, true] { + if unsupported_batch { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.history_input = None; + let mut calls = history::decode_calls(&state.operations[0].call_data).unwrap(); + calls.push(( + RECIPIENT.parse().unwrap(), + Bytes::from_static(&[1, 2, 3, 4]), + )); + state.operations[0].call_data = account::batch(&calls); + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 1); + assert_eq!(history[0].amount, 77); + assert!(!history[0].is_incoming); + assert_eq!(history[0].user_operation_hash, None); + assert_eq!(history[0].recipient, RECIPIENT); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), 1); + } +} diff --git a/src/modules/usdt/transaction.rs b/src/modules/usdt/transaction.rs new file mode 100644 index 0000000..b4ec33e --- /dev/null +++ b/src/modules/usdt/transaction.rs @@ -0,0 +1,91 @@ +use super::{user_operation::UserOperation, UsdtError}; +use alloy_primitives::{Bytes, B256}; +use alloy_sol_types::sol; +use bitcoin::secp256k1::SecretKey; +use serde::{Deserialize, Serialize}; + +sol! { + struct PackedOperation { address sender; uint256 nonce; bytes initCode; bytes callData; bytes32 accountGasLimits; uint256 preVerificationGas; bytes32 gasFees; bytes paymasterAndData; bytes signature; } + #[derive(Debug)] + struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; } + #[derive(Debug)] + struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; } + struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; } + struct OFTFeeDetail { int256 feeAmountLD; string description; } + struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; } + interface Oft { + function token() external view returns (address); + function peers(uint32 eid) external view returns (bytes32); + function quoteOFT(SendParam param) external view returns (OFTLimit limit, OFTFeeDetail[] fees, OFTReceipt receipt); + function quoteSend(SendParam param, bool payInLzToken) external view returns (MessagingFee fee); + event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD); + } + interface BridgeHelper { + function token() external view returns (address); + function maxGas() external view returns (uint256); + function quoteSend(SendParam param, MessagingFee fee) external view returns (uint256 totalAmount); + function send(address oft, SendParam param, MessagingFee fee) external payable; + event LogSend(address indexed sender, address indexed oft, uint256 amountLD, uint256 nativeFee, uint256 feeInToken, uint256 totalAmount); + } + interface EntryPoint { + function getNonce(address sender, uint192 key) view returns (uint256); + function handleOps(PackedOperation[] ops, address beneficiary); + event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed); + } + interface Paymaster { + event UserOperationSponsored(bytes32 indexed userOpHash, address indexed user, uint8 paymasterMode, address token, uint256 tokenAmountPaid, uint256 exchangeRate); + } + interface Erc20 { + function approve(address spender, uint256 amount) returns (bool); + function transfer(address recipient, uint256 amount) returns (bool); + function balanceOf(address owner) view returns (uint256); + event Transfer(address indexed from, address indexed to, uint256 value); + } +} + +pub(super) fn operation_logs( + receipt: &serde_json::Value, + hash: B256, +) -> Result<&[serde_json::Value], UsdtError> { + use alloy_sol_types::SolEvent; + let logs = receipt["logs"] + .as_array() + .ok_or(UsdtError::InvalidResponse)?; + let mut start = 0; + for (index, log) in logs.iter().enumerate() { + let address: alloy_primitives::Address = serde_json::from_value(log["address"].clone())?; + if address != super::account::ENTRY_POINT { + continue; + } + if let Ok(event) = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) { + if event.userOpHash == hash { + return Ok(&logs[start..=index]); + } + start = index + 1; + } + } + Err(UsdtError::InvalidResponse) +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub(super) struct Plan { + pub operation: UserOperation, + pub created_block: u64, + pub expires_at: u64, + pub bridge_fee: u64, +} + +impl Plan { + pub fn sign(&mut self, key: &SecretKey) -> Result<(B256, String), UsdtError> { + let hash = self.operation.sign(key, super::types::CHAIN_ID)?; + Ok((hash, serde_json::to_string(self)?)) + } +} + +pub(super) fn event_data( + value: &serde_json::Value, +) -> Result { + let topics: Vec = serde_json::from_value(value["topics"].clone())?; + let data: Bytes = serde_json::from_value(value["data"].clone())?; + alloy_primitives::LogData::new(topics, data).ok_or(UsdtError::InvalidResponse) +} diff --git a/src/modules/usdt/types.rs b/src/modules/usdt/types.rs new file mode 100644 index 0000000..08e28da --- /dev/null +++ b/src/modules/usdt/types.rs @@ -0,0 +1,74 @@ +use alloy_primitives::{address, Address}; +use serde::{Deserialize, Serialize}; + +pub(super) const CHAIN_ID: u64 = 42161; +pub(super) const TOKEN: Address = address!("Fd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"); +pub(super) const OFT: Address = address!("14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"); +pub(super) const BRIDGE_HELPER: Address = address!("a90f03c856D01F698E7071B393387cd75a8a319A"); +pub(super) const EXPLORER: &str = "https://arbiscan.io"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] +pub enum UsdtDestination { + Stable, + Ethereum, + Arbitrum, + Polygon, + Plasma, +} + +impl UsdtDestination { + pub(super) fn endpoint(self) -> Option { + match self { + Self::Stable => Some(30396), + Self::Ethereum => Some(30101), + Self::Arbitrum => None, + Self::Polygon => Some(30109), + Self::Plasma => Some(30383), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] +pub struct UsdtPaymentRequest { + pub recipient: String, + pub amount: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, uniffi::Record)] +pub struct UsdtQuote { + pub id: String, + pub recipient: String, + pub destination: UsdtDestination, + pub amount: u64, + pub received_amount: u64, + pub maximum_fee: u64, + pub expires_at: u64, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] +pub enum UsdtTransferStatus { + Pending, + Confirmed, + Failed, + Bridging, + BridgeNeedsAttention, + Replaced, +} + +#[derive(Clone, Debug, Serialize, Deserialize, uniffi::Record)] +pub struct UsdtTransfer { + pub id: String, + pub tx_hash: String, + pub user_operation_hash: Option, + pub bridge_guid: Option, + pub recipient: String, + pub destination: UsdtDestination, + pub amount: u64, + pub received_amount: u64, + pub bridge_fee: u64, + pub fee: Option, + pub is_incoming: bool, + pub status: UsdtTransferStatus, + pub timestamp: u64, + pub explorer_url: String, +} diff --git a/src/modules/usdt/user_operation.rs b/src/modules/usdt/user_operation.rs new file mode 100644 index 0000000..9dda977 --- /dev/null +++ b/src/modules/usdt/user_operation.rs @@ -0,0 +1,179 @@ +use super::{ + account::{DELEGATE, ENTRY_POINT}, + keys::key_address, + types::CHAIN_ID, + UsdtError, +}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rlp::Encodable; +use alloy_sol_types::{eip712_domain, sol, SolStruct}; +use bitcoin::secp256k1::{Message, Secp256k1, SecretKey}; +use serde::{Deserialize, Serialize}; + +sol! { + struct PackedUserOperation { + address sender; + uint256 nonce; + bytes initCode; + bytes callData; + bytes32 accountGasLimits; + uint256 preVerificationGas; + bytes32 gasFees; + bytes paymasterAndData; + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(super) struct Authorization { + pub chain_id: U256, + pub address: Address, + pub nonce: U256, + pub y_parity: U256, + pub r: B256, + pub s: B256, +} + +impl Authorization { + pub fn dummy(nonce: u64) -> Self { + Self { + chain_id: U256::from(CHAIN_ID), + address: DELEGATE, + nonce: U256::from(nonce), + y_parity: U256::ZERO, + r: B256::repeat_byte(0x11), + s: B256::repeat_byte(0x22), + } + } + + pub fn hash(&self) -> Result { + let nonce: u64 = self + .nonce + .try_into() + .map_err(|_| UsdtError::InvalidResponse)?; + if self.chain_id != U256::from(CHAIN_ID) || self.address != DELEGATE || nonce == u64::MAX { + return Err(UsdtError::InvalidResponse); + } + let mut payload = Vec::new(); + CHAIN_ID.encode(&mut payload); + self.address.as_slice().encode(&mut payload); + nonce.encode(&mut payload); + let mut encoded = vec![0x05]; + alloy_rlp::Header { + list: true, + payload_length: payload.len(), + } + .encode(&mut encoded); + encoded.extend(payload); + Ok(alloy_primitives::keccak256(encoded)) + } + + fn sign(&mut self, key: &SecretKey) -> Result<(), UsdtError> { + let signature = sign_hash(self.hash()?, key)?; + self.r = B256::from_slice(&signature[..32]); + self.s = B256::from_slice(&signature[32..64]); + self.y_parity = U256::from(signature[64] - 27); + Ok(()) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(super) struct UserOperation { + pub sender: Address, + pub nonce: U256, + pub factory: Bytes, + pub factory_data: Bytes, + pub call_data: Bytes, + pub call_gas_limit: U256, + pub verification_gas_limit: U256, + pub pre_verification_gas: U256, + pub max_fee_per_gas: U256, + pub max_priority_fee_per_gas: U256, + pub paymaster: Address, + pub paymaster_verification_gas_limit: U256, + pub paymaster_post_op_gas_limit: U256, + pub paymaster_data: Bytes, + pub signature: Bytes, + pub eip7702_auth: Authorization, +} + +impl UserOperation { + pub fn paymaster_and_data(&self) -> Result { + Ok([ + self.paymaster.as_slice(), + &narrow(self.paymaster_verification_gas_limit)?.to_be_bytes(), + &narrow(self.paymaster_post_op_gas_limit)?.to_be_bytes(), + &self.paymaster_data, + ] + .concat() + .into()) + } + + pub fn hash(&self, chain_id: u64) -> Result { + if self.factory.as_ref() != [0x77, 0x02] || !self.factory_data.is_empty() { + return Err(UsdtError::InvalidResponse); + } + let packed = PackedUserOperation { + sender: self.sender, + nonce: self.nonce, + // EntryPoint replaces the 7702 marker with the active delegate before hashing. + initCode: Bytes::copy_from_slice(DELEGATE.as_slice()), + callData: self.call_data.clone(), + accountGasLimits: pack(self.verification_gas_limit, self.call_gas_limit)?, + preVerificationGas: self.pre_verification_gas, + gasFees: pack(self.max_priority_fee_per_gas, self.max_fee_per_gas)?, + paymasterAndData: self.paymaster_and_data()?, + }; + Ok(packed.eip712_signing_hash(&eip712_domain! { + name: "ERC4337", version: "1", chain_id: chain_id, verifying_contract: ENTRY_POINT, + })) + } + + pub fn sign(&mut self, key: &SecretKey, chain_id: u64) -> Result { + if key_address(key) != self.sender || chain_id != CHAIN_ID { + return Err(UsdtError::InvalidCredentials); + } + self.eip7702_auth.sign(key)?; + let hash = self.hash(chain_id)?; + self.signature = sign_hash(hash, key)?; + Ok(hash) + } + + pub fn maximum_native_cost(&self) -> Result { + let gas = [ + self.call_gas_limit, + self.verification_gas_limit, + self.pre_verification_gas, + self.paymaster_verification_gas_limit, + self.paymaster_post_op_gas_limit, + ] + .into_iter() + .try_fold(U256::ZERO, |total, value| total.checked_add(value)) + .ok_or(UsdtError::InvalidResponse)?; + gas.checked_mul(self.max_fee_per_gas) + .ok_or(UsdtError::InvalidResponse) + } +} + +fn sign_hash(hash: B256, key: &SecretKey) -> Result { + let (recovery, signature) = Secp256k1::new() + .sign_ecdsa_recoverable(&Message::from_digest(hash.0), key) + .serialize_compact(); + if recovery.to_i32() > 1 { + return Err(UsdtError::InvalidCredentials); + } + let mut encoded = signature.to_vec(); + encoded.push(27 + recovery.to_i32() as u8); + Ok(encoded.into()) +} + +fn pack(high: U256, low: U256) -> Result { + Ok(B256::from_slice( + &[narrow(high)?.to_be_bytes(), narrow(low)?.to_be_bytes()].concat(), + )) +} + +fn narrow(value: U256) -> Result { + value.try_into().map_err(|_| UsdtError::InvalidResponse) +} diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs new file mode 100644 index 0000000..1636aa2 --- /dev/null +++ b/src/modules/usdt/wallet.rs @@ -0,0 +1,664 @@ +use super::{ + account::{validate_delegation, ENTRY_POINT}, + amount::token_amount, + keys::{derive_key, parse_address}, + paymaster::{with_margin, Pimlico, PAYMASTER}, + rpc::Rpc, + store::{QuoteData, Store}, + transaction::{ + event_data, BridgeHelper, EntryPoint, Erc20, MessagingFee, Oft, Paymaster, Plan, SendParam, + }, + types::{BRIDGE_HELPER, CHAIN_ID, EXPLORER, OFT, TOKEN}, + user_operation::Authorization, + UsdtDestination, UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus, +}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_sol_types::{SolCall, SolEvent}; +use serde_json::{json, Value}; +use std::sync::{ + atomic::{AtomicU64, AtomicUsize, Ordering}, + Arc, +}; +use tokio::sync::Mutex; + +#[derive(uniffi::Object)] +pub struct UsdtWallet { + pub(super) address: Address, + pub(super) rpc: Rpc, + pub(super) paymaster: Pimlico, + pub(super) store: Store, + operation: Mutex<()>, + bridge_poll_offset: AtomicUsize, + pub(super) history_range_limit: AtomicU64, +} + +#[uniffi::export(async_runtime = "tokio")] +impl UsdtWallet { + #[uniffi::constructor] + pub fn new( + address: String, + storage_path: String, + rpc_url: String, + bundler_url: String, + ) -> Result, UsdtError> { + if rpc_url.is_empty() || bundler_url.is_empty() { + return Err(UsdtError::NotConfigured); + } + let address = parse_address(&address)?; + let store = Store::open(&storage_path, &format!("{CHAIN_ID}:{}", address))?; + let rpc = Rpc::new(rpc_url, CHAIN_ID)?; + let paymaster = Pimlico { + rpc: rpc.with_url(bundler_url)?, + }; + Ok(Arc::new(Self { + address, + rpc, + paymaster, + store, + operation: Mutex::new(()), + bridge_poll_offset: AtomicUsize::new(0), + history_range_limit: AtomicU64::new(10_000_000), + })) + } + + pub fn receive_address(&self) -> String { + self.address.to_checksum(None) + } + + pub fn receive_uri(&self) -> String { + format!( + "ethereum:{}@{CHAIN_ID}/transfer?address={}", + TOKEN.to_checksum(None), + self.receive_address() + ) + } + + pub async fn balance(&self) -> Result { + self.rpc.verify_chain().await?; + token_amount(self.token_balance().await?) + } + + pub async fn quote_transfer( + &self, + recipient: String, + amount: u64, + destination: UsdtDestination, + ) -> Result { + if amount == 0 { + return Err(UsdtError::InvalidAmount); + } + let recipient = parse_address(recipient.trim())?; + if recipient == self.address + || (destination == UsdtDestination::Arbitrum && recipient == TOKEN) + { + return Err(UsdtError::InvalidAddress); + } + self.store.require_no_pending()?; + self.rpc.verify_chain().await?; + self.require_balance(amount, 0).await?; + let (calls, received_amount, bridge_fee) = + self.transfer_calls(recipient, amount, destination).await?; + if bridge_fee > 0 { + self.require_balance(amount, bridge_fee).await?; + } + let nonce = self.nonce("latest").await?; + let authorization = self.authorization().await?; + let created_block = self.block_number().await?; + let timestamp = self.block_timestamp(created_block).await?; + let (operation, gas_fee, operation_expires_at) = self + .paymaster + .prepare(self.address, nonce, authorization, &calls, timestamp) + .await?; + let expires_at = + now().saturating_add(operation_expires_at.saturating_sub(timestamp).min(120)); + let maximum_fee = gas_fee + .checked_add(bridge_fee) + .ok_or(UsdtError::InvalidAmount)?; + self.require_balance(amount, maximum_fee).await?; + let quote = UsdtQuote { + id: uuid::Uuid::new_v4().to_string(), + recipient: recipient.to_checksum(None), + destination, + amount, + received_amount, + maximum_fee, + expires_at, + }; + self.store.save_quote(&QuoteData { + quote: quote.clone(), + plan: Plan { + operation, + created_block, + expires_at: operation_expires_at, + bridge_fee, + }, + })?; + Ok(quote) + } + + pub async fn send( + &self, + quote_id: String, + mnemonic: String, + passphrase: Option, + ) -> Result { + let _guard = self.operation.lock().await; + let key = derive_key(mnemonic, passphrase)?; + if super::keys::key_address(&key) != self.address { + return Err(UsdtError::InvalidCredentials); + } + if let Some(existing) = self.store.transfer("e_id)? { + return Ok(existing); + } + self.store.require_no_pending()?; + let mut data = self.store.quote("e_id)?; + if data.quote.expires_at <= now() + 5 { + return Err(UsdtError::QuoteExpired); + } + self.rpc.verify_chain().await?; + if self.nonce("latest").await? != data.plan.operation.nonce { + return Err(UsdtError::QuoteExpired); + } + let authorization = self.authorization().await?; + if authorization.nonce != data.plan.operation.eip7702_auth.nonce { + return Err(UsdtError::QuoteExpired); + } + self.require_balance(data.quote.amount, data.quote.maximum_fee) + .await?; + self.paymaster.validate_gas(&data.plan.operation).await?; + if self + .block_timestamp(self.block_number().await?) + .await? + .saturating_add(5) + >= data.plan.expires_at + { + return Err(UsdtError::QuoteExpired); + } + if data.quote.expires_at <= now() + 5 { + return Err(UsdtError::QuoteExpired); + } + let (hash, raw) = data.plan.sign(&key)?; + let transfer = UsdtTransfer { + id: quote_id, + tx_hash: String::new(), + user_operation_hash: Some(format!("{hash:#x}")), + bridge_guid: None, + recipient: data.quote.recipient, + destination: data.quote.destination, + amount: data.quote.amount, + received_amount: data.quote.received_amount, + bridge_fee: data.plan.bridge_fee, + fee: None, + is_incoming: false, + status: UsdtTransferStatus::Pending, + timestamp: now(), + explorer_url: String::new(), + }; + self.store.record_signed(&transfer, &raw)?; + // After persistence a lost response is indeterminate. Retry only the identical signed operation. + let _ = self.broadcast(&data.plan, hash).await; + Ok(transfer) + } + + pub async fn sync_history(&self) -> Result { + let _guard = self.operation.lock().await; + self.rpc.verify_chain().await?; + match tokio::time::timeout(std::time::Duration::from_secs(20), self.scan_history()).await { + Ok(result) => result.map(|()| true), + Err(_) => Ok(false), + } + } + + pub fn history(&self) -> Result, UsdtError> { + self.store.transfers() + } + + pub async fn refresh_transfers(&self) -> Result, UsdtError> { + let _guard = self.operation.lock().await; + let transfers = self.store.unsettled()?; + if transfers.is_empty() { + return self.history(); + } + self.rpc.verify_chain().await?; + self.refresh_bridges(&transfers).await?; + for mut transfer in transfers { + if matches!( + transfer.status, + UsdtTransferStatus::Bridging | UsdtTransferStatus::BridgeNeedsAttention + ) { + continue; + } + let Some(plan) = self.store.pending_plan(&transfer.id)? else { + continue; + }; + let hash = plan.operation.hash(CHAIN_ID)?; + let confirmed_tip = self.block_number().await?.saturating_sub(2); + if confirmed_tip < plan.created_block { + continue; + } + let end = self.pending_search_end(&plan, confirmed_tip).await?; + let logs: Vec = match self.rpc.call("eth_getLogs", json!([{ + "address": ENTRY_POINT, "fromBlock": U256::from(plan.created_block), "toBlock": U256::from(end), + "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, hash, self.address.into_word()] + }])).await { + Ok(logs) => logs, + // The nonce-based lookup below verifies the consuming event within a single block. + Err(UsdtError::LogRangeTooLarge) => Vec::new(), + Err(error) => return Err(error), + }; + if let Some(log) = logs + .iter() + .find(|log| log["removed"].as_bool() != Some(true)) + { + let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) + .map_err(|_| UsdtError::InvalidResponse)?; + if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT + || event.userOpHash != hash + || event.nonce != plan.operation.nonce + { + return Err(UsdtError::InvalidResponse); + } + self.settle_from_log(&mut transfer, log, event).await?; + } else { + let nonce = self.nonce(&format!("0x{confirmed_tip:x}")).await?; + if nonce > plan.operation.nonce { + // A nonce advance alone cannot distinguish this payment from a replacement. + let block = self.nonce_consumed_block(&plan, confirmed_tip).await?; + let candidates: Vec = self.rpc.call("eth_getLogs", json!([{ + "address": ENTRY_POINT, "fromBlock": U256::from(block), "toBlock": U256::from(block), + "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, null, self.address.into_word()] + }])).await?; + for log in candidates + .iter() + .filter(|log| log["removed"].as_bool() != Some(true)) + { + let event = + EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) + .map_err(|_| UsdtError::InvalidResponse)?; + if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT + || u64::try_from(serde_json::from_value::( + log["blockNumber"].clone(), + )?) + .map_err(|_| UsdtError::InvalidResponse)? + != block + || event.sender != self.address + { + return Err(UsdtError::InvalidResponse); + } + if event.nonce != plan.operation.nonce { + continue; + } + if event.userOpHash == hash { + self.settle_from_log(&mut transfer, log, event).await?; + } else { + transfer.status = UsdtTransferStatus::Replaced; + self.store.update_transfer(&transfer)?; + } + break; + } + } else { + let expired = self.block_timestamp(confirmed_tip).await? > plan.expires_at; + if nonce == plan.operation.nonce && expired { + transfer.status = UsdtTransferStatus::Failed; + transfer.fee = Some(0); + self.store.update_transfer(&transfer)?; + } else if !expired { + let _ = self.broadcast(&plan, hash).await; + } + } + } + } + self.history() + } +} + +impl UsdtWallet { + async fn refresh_bridges(&self, transfers: &[UsdtTransfer]) -> Result<(), UsdtError> { + let mut bridges: Vec<_> = transfers + .iter() + .filter(|transfer| { + matches!( + transfer.status, + UsdtTransferStatus::Bridging | UsdtTransferStatus::BridgeNeedsAttention + ) + }) + .collect(); + if bridges.is_empty() { + return Ok(()); + } + let offset = self.bridge_poll_offset.fetch_add(1, Ordering::Relaxed) % bridges.len(); + bridges.rotate_left(offset); + // Rotate the first check so stalled bridges cannot starve later status checks or sends. + let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(5); + for transfer in bridges { + match tokio::time::timeout_at(deadline, self.rpc.bridge_status(transfer)).await { + Ok(Ok(status)) => { + let mut transfer = transfer.clone(); + transfer.status = status; + self.store.update_transfer(&transfer)?; + } + Ok(Err(_)) => {} + Err(_) => break, + } + } + Ok(()) + } + + pub(super) async fn block_number(&self) -> Result { + u64::try_from(self.rpc.call::("eth_blockNumber", json!([])).await?) + .map_err(|_| UsdtError::InvalidResponse) + } + pub(super) async fn block_timestamp(&self, number: u64) -> Result { + let block: Value = self + .rpc + .call("eth_getBlockByNumber", json!([U256::from(number), false])) + .await?; + u64::try_from(serde_json::from_value::(block["timestamp"].clone())?) + .map_err(|_| UsdtError::InvalidResponse) + } + async fn token_balance(&self) -> Result { + self.rpc + .contract( + TOKEN, + Erc20::balanceOfCall { + owner: self.address, + }, + ) + .await + } + async fn require_balance(&self, amount: u64, fee: u64) -> Result<(), UsdtError> { + let required = U256::from(amount) + U256::from(fee); + if self.token_balance().await? < required { + return Err(UsdtError::InsufficientBalance); + } + Ok(()) + } + async fn nonce(&self, block: &str) -> Result { + let bytes: Bytes = self.rpc.call("eth_call", json!([{"to":ENTRY_POINT,"data":Bytes::from(EntryPoint::getNonceCall { sender:self.address, key:Default::default() }.abi_encode())}, block])).await?; + EntryPoint::getNonceCall::abi_decode_returns(&bytes).map_err(|_| UsdtError::InvalidResponse) + } + async fn authorization(&self) -> Result { + let code: Bytes = self + .rpc + .call("eth_getCode", json!([self.address, "latest"])) + .await?; + validate_delegation(&code)?; + let nonce: U256 = self + .rpc + .call("eth_getTransactionCount", json!([self.address, "latest"])) + .await?; + Ok(Authorization::dummy( + nonce.try_into().map_err(|_| UsdtError::InvalidResponse)?, + )) + } + + async fn broadcast(&self, plan: &Plan, expected_hash: B256) -> Result<(), UsdtError> { + if self.authorization().await?.nonce != plan.operation.eip7702_auth.nonce { + return Err(UsdtError::QuoteExpired); + } + let hash: B256 = self + .paymaster + .rpc + .call( + "eth_sendUserOperation", + json!([plan.operation, ENTRY_POINT]), + ) + .await?; + if hash != expected_hash { + return Err(UsdtError::InvalidResponse); + } + Ok(()) + } + async fn settle_from_log( + &self, + transfer: &mut UsdtTransfer, + log: &Value, + event: EntryPoint::UserOperationEvent, + ) -> Result<(), UsdtError> { + if event.sender != self.address || event.paymaster != PAYMASTER { + return Err(UsdtError::InvalidResponse); + } + transfer.tx_hash = serde_json::from_value(log["transactionHash"].clone())?; + transfer.explorer_url = format!("{EXPLORER}/tx/{}", transfer.tx_hash); + let receipt = self + .rpc + .call("eth_getTransactionReceipt", json!([transfer.tx_hash])) + .await?; + self.settle(transfer, &receipt, event.success)?; + self.store.update_transfer(transfer) + } + + async fn nonce_consumed_block(&self, plan: &Plan, tip: u64) -> Result { + // EntryPoint nonces only increase. Locate the consuming block without a months-long log query. + let mut low = plan.created_block; + let mut high = tip; + while low < high { + let mid = low + (high - low) / 2; + if self.nonce(&format!("0x{mid:x}")).await? > plan.operation.nonce { + high = mid; + } else { + low = mid + 1; + } + } + Ok(low) + } + + async fn pending_search_end(&self, plan: &Plan, tip: u64) -> Result { + // The paymaster validity window bounds recovery even after a long absence. + if tip <= plan.created_block + 4096 { + return Ok(tip); + } + let mut low = plan.created_block; + let mut high = tip; + while low + 1 < high { + let mid = low + (high - low) / 2; + if self.block_timestamp(mid).await? <= plan.expires_at { + low = mid; + } else { + high = mid; + } + } + Ok(high) + } + pub(super) fn settle( + &self, + transfer: &mut UsdtTransfer, + receipt: &Value, + success: bool, + ) -> Result<(), UsdtError> { + let operation_hash: B256 = transfer + .user_operation_hash + .as_ref() + .ok_or(UsdtError::InvalidResponse)? + .parse() + .map_err(|_| UsdtError::InvalidResponse)?; + let logs = super::transaction::operation_logs(receipt, operation_hash)?; + let mut gas_fee = None; + let mut bridge_fee = None; + for log in logs { + let address: Address = serde_json::from_value(log["address"].clone())?; + let data = event_data(log)?; + if address == PAYMASTER { + if let Ok(event) = Paymaster::UserOperationSponsored::decode_log_data(&data) { + if event.userOpHash == operation_hash + && event.user == self.address + && event.token == TOKEN + && event.paymasterMode == 1 + { + gas_fee = Some(token_amount(event.tokenAmountPaid)?); + } + } + } + if success && address == BRIDGE_HELPER { + if let Ok(event) = BridgeHelper::LogSend::decode_log_data(&data) { + if event.sender == self.address + && event.oft == OFT + && event.amountLD == U256::from(transfer.amount) + { + bridge_fee = Some(token_amount(event.feeInToken)?); + } + } + } + if success && address == OFT { + if let Ok(event) = Oft::OFTSent::decode_log_data(&data) { + if event.fromAddress == BRIDGE_HELPER + && transfer.destination.endpoint() == Some(event.dstEid) + && event.amountSentLD == U256::from(transfer.amount) + { + transfer.bridge_guid = Some(format!("{:#x}", event.guid)); + transfer.received_amount = token_amount(event.amountReceivedLD)?; + } + } + } + } + transfer.bridge_fee = bridge_fee.unwrap_or(0); + transfer.fee = gas_fee.and_then(|fee| fee.checked_add(transfer.bridge_fee)); + transfer.status = if !success { + UsdtTransferStatus::Failed + } else if transfer.destination == UsdtDestination::Arbitrum { + UsdtTransferStatus::Confirmed + } else if transfer.bridge_guid.is_some() { + UsdtTransferStatus::Bridging + } else { + UsdtTransferStatus::BridgeNeedsAttention + }; + Ok(()) + } + async fn transfer_calls( + &self, + recipient: Address, + amount: u64, + destination: UsdtDestination, + ) -> Result<(Vec<(Address, Bytes)>, u64, u64), UsdtError> { + let Some(eid) = destination.endpoint() else { + return Ok(( + vec![( + TOKEN, + Erc20::transferCall { + recipient, + amount: U256::from(amount), + } + .abi_encode() + .into(), + )], + amount, + 0, + )); + }; + let token = self.rpc.contract(OFT, Oft::tokenCall {}).await?; + let helper_token = self + .rpc + .contract(BRIDGE_HELPER, BridgeHelper::tokenCall {}) + .await?; + let peer = self.rpc.contract(OFT, Oft::peersCall { eid }).await?; + if token != TOKEN || helper_token != TOKEN || peer.is_zero() { + return Err(UsdtError::UnsupportedRoute); + } + let mut param = SendParam { + dstEid: eid, + to: recipient.into_word(), + amountLD: U256::from(amount), + minAmountLD: U256::ZERO, + extraOptions: Bytes::new(), + composeMsg: Bytes::new(), + oftCmd: Bytes::new(), + }; + let oft = self + .rpc + .contract( + OFT, + Oft::quoteOFTCall { + param: param.clone(), + }, + ) + .await?; + if U256::from(amount) < oft.limit.minAmountLD + || U256::from(amount) > oft.limit.maxAmountLD + || oft.receipt.amountSentLD != U256::from(amount) + || oft.receipt.amountReceivedLD.is_zero() + || oft.receipt.amountReceivedLD > U256::from(amount) + { + return Err(UsdtError::InvalidAmount); + } + param.minAmountLD = oft.receipt.amountReceivedLD; + let fee = self + .rpc + .contract( + OFT, + Oft::quoteSendCall { + param: param.clone(), + payInLzToken: false, + }, + ) + .await?; + let maximum_native = self + .rpc + .contract(BRIDGE_HELPER, BridgeHelper::maxGasCall {}) + .await?; + if !fee.lzTokenFee.is_zero() + || fee.nativeFee > maximum_native + || self.rpc.balance(BRIDGE_HELPER).await? < fee.nativeFee + { + return Err(UsdtError::UnsupportedRoute); + } + let total = self + .rpc + .contract( + BRIDGE_HELPER, + BridgeHelper::quoteSendCall { + param: param.clone(), + fee: fee.clone(), + }, + ) + .await?; + let fee = MessagingFee { + nativeFee: fee.nativeFee, + lzTokenFee: U256::ZERO, + }; + let token_fee = total + .checked_sub(U256::from(amount)) + .ok_or(UsdtError::InvalidResponse)?; + let token_fee = with_margin(token_fee)?; + let approval = U256::from(amount) + .checked_add(token_fee) + .ok_or(UsdtError::InvalidResponse)?; + Ok(( + vec![ + ( + TOKEN, + Erc20::approveCall { + spender: BRIDGE_HELPER, + amount: approval, + } + .abi_encode() + .into(), + ), + ( + BRIDGE_HELPER, + BridgeHelper::sendCall { + oft: OFT, + param, + fee, + } + .abi_encode() + .into(), + ), + ( + TOKEN, + Erc20::approveCall { + spender: BRIDGE_HELPER, + amount: U256::ZERO, + } + .abi_encode() + .into(), + ), + ], + token_amount(oft.receipt.amountReceivedLD)?, + token_amount(token_fee)?, + )) + } +} + +pub(super) fn now() -> u64 { + chrono::Utc::now().timestamp().max(0) as u64 +} diff --git a/tests/usdt-fork/package-lock.json b/tests/usdt-fork/package-lock.json new file mode 100644 index 0000000..7148033 --- /dev/null +++ b/tests/usdt-fork/package-lock.json @@ -0,0 +1,119 @@ +{ + "name": "bitkit-usdt-contract-tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bitkit-usdt-contract-tests", + "dependencies": { + "ethers": "6.17.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ethers": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.17.0.tgz", + "integrity": "sha512-BpyrpIPJ3ydEVow8zGaz1DuPS7YU8DcWxuBnY9a0UA/lvAPwrMr+EPXsfrul628SRaekPNeIM4UFh/91GWZang==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.11.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.21.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/tests/usdt-fork/package.json b/tests/usdt-fork/package.json new file mode 100644 index 0000000..bead69b --- /dev/null +++ b/tests/usdt-fork/package.json @@ -0,0 +1,7 @@ +{ + "name": "bitkit-usdt-contract-tests", + "private": true, + "type": "module", + "dependencies": { "ethers": "6.17.0" }, + "scripts": { "start": "node provider.mjs" } +} diff --git a/tests/usdt-fork/provider.mjs b/tests/usdt-fork/provider.mjs new file mode 100644 index 0000000..e4650a3 --- /dev/null +++ b/tests/usdt-fork/provider.mjs @@ -0,0 +1,216 @@ +import assert from 'node:assert/strict'; +import { createServer } from 'node:http'; +import { readFileSync } from 'node:fs'; +import { + AbiCoder, + Contract, + HDNodeWallet, + JsonRpcProvider, + concat, + getBytes, + keccak256, + toBeHex, + zeroPadValue, +} from 'ethers'; + +// A local service fixture using the deployed contracts with a test paymaster signer. +// It does not model Pimlico's pricing, gas estimation, or service acceptance policy. +const rpc = new JsonRpcProvider('http://127.0.0.1:18545', 42161, { + staticNetwork: true, + cacheTimeout: -1, +}); +assert.match(await rpc.send('web3_clientVersion', []), /anvil/i); +assert.equal(await rpc.send('eth_chainId', []), '0xa4b1'); +const signer = HDNodeWallet.fromPhrase( + 'test test test test test test test test test test test junk', + undefined, + "m/44'/60'/0'/0/1", +); +const bundler = await rpc.getSigner(signer.address); +const vector = JSON.parse( + readFileSync(new URL('../../src/modules/usdt/fixtures/eip7702-vectors.json', import.meta.url)), +); +const account = vector.address; +const delegate = '0xe6Cae83BdE06E4c305530e199D7217f42808555B'; +const tokenAddress = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9'; +const pmAddress = '0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402'; +const entryAddress = '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108'; +const packedType = + '(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData,bytes signature)'; +const token = new Contract( + tokenAddress, + ['function balanceOf(address) view returns (uint256)'], + rpc, +); +const pm = new Contract( + pmAddress, + [ + `function getHash(uint8,${packedType}) view returns (bytes32)`, + 'function signers(address) view returns (bool)', + ], + rpc, +); +const entry = new Contract( + entryAddress, + [ + `function handleOps(${packedType}[],address)`, + `function getUserOpHash(${packedType}) view returns (bytes32)`, + 'function depositTo(address) payable', + ], + bundler, +); +const coder = AbiCoder.defaultAbiCoder(); + +async function setMapping(contract, account, value, check) { + for (let slot = 0; slot < 100; slot++) { + const key = keccak256(coder.encode(['address', 'uint256'], [account, slot])); + const previous = await rpc.send('eth_getStorageAt', [contract, key, 'latest']); + await rpc.send('anvil_setStorageAt', [contract, key, toBeHex(value, 32)]); + if (await check()) return slot; + await rpc.send('anvil_setStorageAt', [contract, key, previous]); + } + throw Error('Could not locate fixture mapping'); +} +await rpc.send('anvil_setCode', [account, '0x']); +await rpc.send('anvil_setNonce', [account, '0x0']); +await rpc.send('anvil_setBalance', [account, '0x0']); +const balanceSlot = await setMapping( + tokenAddress, + account, + 1_000_000_000n, + async () => (await token.balanceOf(account)) === 1_000_000_000n, +); +await setMapping(pmAddress, signer.address, 1n, async () => await pm.signers(signer.address)); +await (await entry.depositTo(pmAddress, { value: 10n ** 18n })).wait(); + +const gas = { + callGasLimit: toBeHex(1_500_000), + verificationGasLimit: toBeHex(800_000), + preVerificationGas: toBeHex(100_000), + paymasterVerificationGasLimit: toBeHex(100_000), + paymasterPostOpGasLimit: toBeHex(100_000), +}; +const rate = 3_000_000_000n; +function pack(op) { + return { + sender: op.sender, + nonce: op.nonce, + initCode: concat(['0x7702', toBeHex(0, 18)]), + callData: op.callData, + accountGasLimits: concat([toBeHex(op.verificationGasLimit, 16), toBeHex(op.callGasLimit, 16)]), + preVerificationGas: op.preVerificationGas, + gasFees: concat([toBeHex(op.maxPriorityFeePerGas, 16), toBeHex(op.maxFeePerGas, 16)]), + paymasterAndData: concat([ + op.paymaster, + toBeHex(op.paymasterVerificationGasLimit, 16), + toBeHex(op.paymasterPostOpGasLimit, 16), + op.paymasterData, + ]), + signature: op.signature, + }; +} +async function dispatch(method, params) { + if (method === 'eth_estimateUserOperationGas' || method === 'eth_sendUserOperation') { + const op = params[0]; + assert.equal(op.factory, '0x7702'); + assert.ok(op.eip7702Auth, 'The 7702 factory marker requires authorization'); + assert.equal( + BigInt(op.eip7702Auth.nonce), + BigInt(await rpc.send('eth_getTransactionCount', [op.sender, 'latest'])), + 'Authorization must use the current account nonce', + ); + } + if (method === 'test_fundWallet') { + const key = keccak256(coder.encode(['address', 'uint256'], [params[0], balanceSlot])); + await rpc.send('anvil_setStorageAt', [tokenAddress, key, toBeHex(1_000_000_000n, 32)]); + return true; + } + if (method === 'pimlico_getTokenQuotes') + return { + quotes: [ + { + token: tokenAddress, + paymaster: pmAddress, + postOpGas: toBeHex(50000), + exchangeRate: toBeHex(rate), + }, + ], + }; + if (method === 'pimlico_getUserOperationGasPrice') + return { + fast: { maxFeePerGas: toBeHex(100_000_000), maxPriorityFeePerGas: toBeHex(1_000_000) }, + }; + if (method === 'eth_estimateUserOperationGas') return gas; + if (method === 'pm_getPaymasterData' || method === 'pm_getPaymasterStubData') { + const stub = method === 'pm_getPaymasterStubData'; + const limits = stub ? { paymasterPostOpGasLimit: gas.paymasterPostOpGasLimit } : {}; + const op = { ...params[0], paymaster: pmAddress, ...limits }; + const unsigned = concat([ + '0x0300', + toBeHex(Math.floor(Date.now() / 1000) + 600, 6), + toBeHex(0, 6), + tokenAddress, + toBeHex(50000, 16), + toBeHex(rate, 32), + toBeHex(100000, 16), + signer.address, + ]); + op.paymasterData = concat([unsigned, zeroPadValue('0x01', 65)]); + const signature = await signer.signMessage(getBytes(await pm.getHash(1, pack(op)))); + return { paymaster: pmAddress, paymasterData: concat([unsigned, signature]), ...limits }; + } + if (method === 'eth_sendUserOperation') { + const op = pack(params[0]); + const hash = await rpc.send('eth_call', [ + { to: entryAddress, data: entry.interface.encodeFunctionData('getUserOpHash', [op]) }, + 'latest', + { [op.sender]: { code: concat(['0xef0100', delegate]) } }, + ]); + const auth = params[0].eip7702Auth; + const options = { + gasLimit: 6_000_000, + type: 4, + authorizationList: [ + { + address: auth.address, + chainId: BigInt(auth.chainId), + nonce: BigInt(auth.nonce), + signature: { r: auth.r, s: auth.s, yParity: Number(BigInt(auth.yParity)) }, + }, + ], + }; + const tx = await entry.handleOps([op], signer.address, options); + const receipt = await tx.wait(); + await rpc.send('anvil_mine', [3]); + console.log( + JSON.stringify({ + operation: hash, + transaction: receipt.hash, + gasUsed: receipt.gasUsed.toString(), + }), + ); + return hash; + } + return rpc.send(method, params); +} +const server = createServer(async (request, response) => { + let body = ''; + for await (const chunk of request) body += chunk; + const call = JSON.parse(body); + let result; + try { + result = { jsonrpc: '2.0', id: call.id, result: await dispatch(call.method, call.params) }; + } catch (error) { + console.error(call.method, error.shortMessage ?? error.message, error.data ?? ''); + result = { + jsonrpc: '2.0', + id: call.id, + error: { code: -32000, message: error.shortMessage ?? error.message }, + }; + } + response.writeHead(200, { 'content-type': 'application/json' }); + response.end(JSON.stringify(result)); +}); +server.listen(18546, '127.0.0.1', () => + console.log('Fork provider ready on localhost:18546; no mainnet writes.'), +); diff --git a/tests/usdt-fork/vectors.mjs b/tests/usdt-fork/vectors.mjs new file mode 100644 index 0000000..9581f8b --- /dev/null +++ b/tests/usdt-fork/vectors.mjs @@ -0,0 +1,122 @@ +import { writeFileSync } from 'node:fs'; +import { + HDNodeWallet, + Interface, + TypedDataEncoder, + concat, + hashAuthorization, + toBeHex, +} from 'ethers'; + +const wallet = HDNodeWallet.fromPhrase( + 'test test test test test test test test test test test junk', +); +const token = '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9'; +const paymaster = '0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402'; +const delegate = '0xe6Cae83BdE06E4c305530e199D7217f42808555B'; +const entryPoint = '0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108'; +const tokenAbi = new Interface([ + 'function approve(address,uint256)', + 'function transfer(address,uint256)', +]); +const accountAbi = new Interface([ + 'function executeBatch((address target,uint256 value,bytes data)[] calls)', +]); +const calls = [ + { to: token, data: tokenAbi.encodeFunctionData('approve', [paymaster, 100000]) }, + { + to: token, + data: tokenAbi.encodeFunctionData('transfer', [ + '0x1111111111111111111111111111111111111111', + 1000000, + ]), + }, +]; +const authorization = { chainId: 42161, address: delegate, nonce: 0 }; +const authHash = hashAuthorization(authorization); +const authSignature = wallet.signingKey.sign(authHash); +const op = { + sender: wallet.address, + nonce: '0x0', + factory: '0x7702', + factoryData: '0x', + callData: accountAbi.encodeFunctionData('executeBatch', [calls.map((c) => [c.to, 0, c.data])]), + callGasLimit: '0x249f0', + verificationGasLimit: '0x3d090', + preVerificationGas: '0x13880', + maxFeePerGas: '0x2faf080', + maxPriorityFeePerGas: '0x186a0', + paymaster, + paymasterVerificationGasLimit: '0x186a0', + paymasterPostOpGasLimit: '0xc350', + paymasterData: concat([ + '0x0300', + toBeHex(1800000000, 6), + toBeHex(0, 6), + token, + toBeHex(50000, 16), + toBeHex(3000000000, 32), + toBeHex(100000, 16), + wallet.address, + toBeHex(1, 65), + ]), + signature: '0x', + eip7702Auth: { + chainId: '0xa4b1', + address: delegate, + nonce: '0x0', + yParity: toBeHex(authSignature.yParity), + r: authSignature.r, + s: authSignature.s, + }, +}; +const types = { + PackedUserOperation: [ + ['sender', 'address'], + ['nonce', 'uint256'], + ['initCode', 'bytes'], + ['callData', 'bytes'], + ['accountGasLimits', 'bytes32'], + ['preVerificationGas', 'uint256'], + ['gasFees', 'bytes32'], + ['paymasterAndData', 'bytes'], + ].map(([name, type]) => ({ name, type })), +}; +const packed = { + sender: op.sender, + nonce: op.nonce, + initCode: delegate, + callData: op.callData, + accountGasLimits: concat([toBeHex(op.verificationGasLimit, 16), toBeHex(op.callGasLimit, 16)]), + preVerificationGas: op.preVerificationGas, + gasFees: concat([toBeHex(op.maxPriorityFeePerGas, 16), toBeHex(op.maxFeePerGas, 16)]), + paymasterAndData: concat([ + paymaster, + toBeHex(op.paymasterVerificationGasLimit, 16), + toBeHex(op.paymasterPostOpGasLimit, 16), + op.paymasterData, + ]), +}; +const hash = TypedDataEncoder.hash( + { name: 'ERC4337', version: '1', chainId: 42161, verifyingContract: entryPoint }, + types, + packed, +); +const signature = wallet.signingKey.sign(hash).serialized; +writeFileSync( + new URL('../../src/modules/usdt/fixtures/eip7702-vectors.json', import.meta.url), + JSON.stringify( + { + source: + 'ethers@6.17.0; EntryPoint v0.8 / Simple7702Account; generated by tests/usdt-fork/vectors.mjs', + address: wallet.address, + calls, + operation: op, + userOperationHash: hash, + signature, + authorizationHash: authHash, + }, + null, + 2, + ) + '\n', +); From f07ae07923912b2ec0df73443c5075e39a49822e Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 24 Sep 2026 18:08:36 +0300 Subject: [PATCH 2/9] fix: harden usdt recovery and fee validation --- .github/workflows/usdt-tests.yml | 35 ++++ Package.swift | 2 +- bindings/ios/bitkitcore.swift | 68 ++++---- src/modules/usdt/README.md | 112 ++++--------- src/modules/usdt/deposits.rs | 78 ++++----- src/modules/usdt/errors.rs | 2 + src/modules/usdt/history.rs | 117 +++++++++++--- src/modules/usdt/keys.rs | 24 ++- src/modules/usdt/paymaster.rs | 56 ++++--- src/modules/usdt/payment_request.rs | 5 +- src/modules/usdt/rpc.rs | 179 +++++++++++++++++---- src/modules/usdt/store.rs | 41 ++++- src/modules/usdt/tests.rs | 238 ++++++++++++++++++++++++++-- src/modules/usdt/transaction.rs | 43 ++++- src/modules/usdt/types.rs | 1 - src/modules/usdt/user_operation.rs | 2 +- src/modules/usdt/wallet.rs | 104 ++++++++++-- 17 files changed, 807 insertions(+), 300 deletions(-) create mode 100644 .github/workflows/usdt-tests.yml diff --git a/.github/workflows/usdt-tests.yml b/.github/workflows/usdt-tests.yml new file mode 100644 index 0000000..f20056a --- /dev/null +++ b/.github/workflows/usdt-tests.yml @@ -0,0 +1,35 @@ +name: USDT Tests + +on: + pull_request: + push: + branches: [master, main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + usdt-tests: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libdbus-1-dev pkg-config protobuf-compiler + - name: Check formatting + run: cargo fmt --check + - name: Run USDT tests + run: cargo test --locked --lib modules::usdt + - name: Check Rust code + run: cargo clippy --locked --lib --tests diff --git a/Package.swift b/Package.swift index 3917d9e..2ea8a85 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "66eefed1d64a6b4804eb8d84e9374cedf6d66fcd0c51728d3a35ceb55d29699d" +let checksum = "eed89e4a6d060e064bc91f8a66115ffff90fff6edd659c42024077c49a8982b2" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index d01e577..c07f0bd 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -16809,7 +16809,6 @@ public struct UsdtTransfer { public var destination: UsdtDestination public var amount: UInt64 public var receivedAmount: UInt64 - public var bridgeFee: UInt64 public var fee: UInt64? public var isIncoming: Bool public var status: UsdtTransferStatus @@ -16818,7 +16817,7 @@ public struct UsdtTransfer { // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, txHash: String, userOperationHash: String?, bridgeGuid: String?, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, bridgeFee: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { + public init(id: String, txHash: String, userOperationHash: String?, bridgeGuid: String?, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { self.id = id self.txHash = txHash self.userOperationHash = userOperationHash @@ -16827,7 +16826,6 @@ public struct UsdtTransfer { self.destination = destination self.amount = amount self.receivedAmount = receivedAmount - self.bridgeFee = bridgeFee self.fee = fee self.isIncoming = isIncoming self.status = status @@ -16867,9 +16865,6 @@ extension UsdtTransfer: Equatable, Hashable { if lhs.receivedAmount != rhs.receivedAmount { return false } - if lhs.bridgeFee != rhs.bridgeFee { - return false - } if lhs.fee != rhs.fee { return false } @@ -16897,7 +16892,6 @@ extension UsdtTransfer: Equatable, Hashable { hasher.combine(destination) hasher.combine(amount) hasher.combine(receivedAmount) - hasher.combine(bridgeFee) hasher.combine(fee) hasher.combine(isIncoming) hasher.combine(status) @@ -16925,7 +16919,6 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { destination: FfiConverterTypeUsdtDestination.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf), receivedAmount: FfiConverterUInt64.read(from: &buf), - bridgeFee: FfiConverterUInt64.read(from: &buf), fee: FfiConverterOptionUInt64.read(from: &buf), isIncoming: FfiConverterBool.read(from: &buf), status: FfiConverterTypeUsdtTransferStatus.read(from: &buf), @@ -16943,7 +16936,6 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { FfiConverterTypeUsdtDestination.write(value.destination, into: &buf) FfiConverterUInt64.write(value.amount, into: &buf) FfiConverterUInt64.write(value.receivedAmount, into: &buf) - FfiConverterUInt64.write(value.bridgeFee, into: &buf) FfiConverterOptionUInt64.write(value.fee, into: &buf) FfiConverterBool.write(value.isIncoming, into: &buf) FfiConverterTypeUsdtTransferStatus.write(value.status, into: &buf) @@ -24266,6 +24258,7 @@ public enum UsdtError: Swift.Error { case InvalidAddress case WrongNetwork case InvalidCredentials + case ClockSkew case UnsupportedDelegation case InsufficientBalance case QuoteExpired @@ -24301,23 +24294,24 @@ public struct FfiConverterTypeUsdtError: FfiConverterRustBuffer { case 2: return .InvalidAddress case 3: return .WrongNetwork case 4: return .InvalidCredentials - case 5: return .UnsupportedDelegation - case 6: return .InsufficientBalance - case 7: return .QuoteExpired - case 8: return .PendingTransfer - case 9: return .UnsupportedRoute - case 10: return .DepositNeedsAttention - case 11: return .NotConfigured - case 12: return .NetworkUnavailable - case 13: return .RateLimited - case 14: return .LogRangeTooLarge - case 15: return .TransactionRejected( + case 5: return .ClockSkew + case 6: return .UnsupportedDelegation + case 7: return .InsufficientBalance + case 8: return .QuoteExpired + case 9: return .PendingTransfer + case 10: return .UnsupportedRoute + case 11: return .DepositNeedsAttention + case 12: return .NotConfigured + case 13: return .NetworkUnavailable + case 14: return .RateLimited + case 15: return .LogRangeTooLarge + case 16: return .TransactionRejected( reason: try FfiConverterString.read(from: &buf) ) - case 16: return .Storage( + case 17: return .Storage( reason: try FfiConverterString.read(from: &buf) ) - case 17: return .InvalidResponse + case 18: return .InvalidResponse default: throw UniffiInternalError.unexpectedEnumCase } @@ -24346,58 +24340,62 @@ public struct FfiConverterTypeUsdtError: FfiConverterRustBuffer { writeInt(&buf, Int32(4)) - case .UnsupportedDelegation: + case .ClockSkew: writeInt(&buf, Int32(5)) - case .InsufficientBalance: + case .UnsupportedDelegation: writeInt(&buf, Int32(6)) - case .QuoteExpired: + case .InsufficientBalance: writeInt(&buf, Int32(7)) - case .PendingTransfer: + case .QuoteExpired: writeInt(&buf, Int32(8)) - case .UnsupportedRoute: + case .PendingTransfer: writeInt(&buf, Int32(9)) - case .DepositNeedsAttention: + case .UnsupportedRoute: writeInt(&buf, Int32(10)) - case .NotConfigured: + case .DepositNeedsAttention: writeInt(&buf, Int32(11)) - case .NetworkUnavailable: + case .NotConfigured: writeInt(&buf, Int32(12)) - case .RateLimited: + case .NetworkUnavailable: writeInt(&buf, Int32(13)) - case .LogRangeTooLarge: + case .RateLimited: writeInt(&buf, Int32(14)) - case let .TransactionRejected(reason): + case .LogRangeTooLarge: writeInt(&buf, Int32(15)) + + + case let .TransactionRejected(reason): + writeInt(&buf, Int32(16)) FfiConverterString.write(reason, into: &buf) case let .Storage(reason): - writeInt(&buf, Int32(16)) + writeInt(&buf, Int32(17)) FfiConverterString.write(reason, into: &buf) case .InvalidResponse: - writeInt(&buf, Int32(17)) + writeInt(&buf, Int32(18)) } } diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index a2004dc..9e9e5b1 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -1,116 +1,60 @@ # USDT on Arbitrum One -Bitkit keeps a separate USDT balance, receive address, authenticated payment flow and activity list. Rust owns account derivation, quotes, signing, persistence and chain access. The native apps retain their existing keychain, authentication, lifecycle and UI components. +`UsdtWallet` owns seed-derived accounts, fee quotes, signing, durable payment recovery and activity. Amounts use millionths of USDT. The native apps own credentials, payment authentication and presentation. -## Account and recovery +## Account and authorization -The receive address is the Ethereum address derived from the existing Bitkit BIP39 mnemonic and passphrase at `m/44'/60'/0'/0/0`. EIP-7702 delegates that address to the pinned Simple7702Account implementation (`0xe6Cae83BdE06E4c305530e199D7217f42808555B`) using EntryPoint 0.8 (`0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108`). +The address derives from the Bitkit BIP39 mnemonic and passphrase at `m/44'/60'/0'/0/0`. Arbitrum One (42161), USDT0, EntryPoint 0.8 (`0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108`) and Simple7702Account (`0xe6Cae83BdE06E4c305530e199D7217f42808555B`) are pinned in code. -Each authenticated payment signs a chain-specific authorization for the pinned delegate at the current account nonce. This keeps the operation hash bound to that delegate and satisfies the bundler’s `0x7702` factory-marker policy, including after activation. Authorization gas is included in the quoted USDT maximum. Quotes use dummy authorization; they never receive a signing key. There is no factory deployment or separate smart-wallet address. +Each payment signs a chain-specific EIP-7702 authorization at the current account nonce and an EntryPoint operation committing to that delegate. Authorization costs are included in the USDT fee. There is no separate smart-wallet address or factory deployment. Another wallet with the same derivation can access the address; gas-payment support may differ. -Restoring the same seed and passphrase reconstructs the same address and reads its balance and activity from Arbitrum. Another wallet using the same derivation can access that address, although its gas-payment and delegation support may differ. +Delegation can persist even when an operation fails. A foreign delegate blocks sends without blocking balance/history reads. Bitkit never silently replaces it. -Delegation persists onchain and can be applied even if the operation fails. Retries retain both signatures and require the authorization nonce to remain current. If authorization was consumed without a known payment result, the signed payment stays pending until an event identifies its outcome or its paymaster terms expire with the EntryPoint nonce unchanged. A different delegate blocks Bitkit sends; balances and incoming/outgoing token history remain readable. Bitkit does not silently replace another wallet's delegation. +Owned mnemonic/passphrase/seed buffers are zeroized and signing keys are erased on drop on a best-effort basis. FFI, compiler and library-internal copies cannot be guaranteed erased. SQLite contains no seed or private key. -The native keychain is the source of signing credentials. Rust zeroizes its owned mnemonic/passphrase/seed buffers and best-effort erases signing keys; FFI and library-internal copies cannot be guaranteed erased; SQLite stores no seed or private key. +## Quotes and fees -Signed operations are stored before transmission. A lost response only retries the identical operation, and a quote ID cannot authorize a second payment. +`quote_transfer` takes a raw recipient, positive atomic amount and destination; it never receives signing credentials. Local quotes last at most 120 seconds and newly quoted paymaster terms must expire within 15 minutes. `send` validates the owner, nonces, balance, gas estimates, current gas prices and deadlines before signing the stored plan. Changed terms require a new review; signing cannot raise the approved fee. -Expiry of the signed paymaster terms with an unchanged on-chain nonce resolves an unmined submission. The shorter local quote deadline cannot release a signed payment. Network timestamps govern these checks. A higher nonce remains pending until an event identifies this payment or its replacement. +The pinned ERC-20 paymaster collects USDT. Its finite approval includes a 5% margin; the displayed maximum fee comes from signed gas limits and paymaster terms, not the allowance. Call/pre-verification estimates receive 10% execution/L1-data headroom; the charged pre-verification margin is included in the maximum. A residual paymaster allowance can remain and is reset to a finite amount on the next payment. -New attempts require another quote and native authorization. One source-chain payment can be pending at a time; confirmed bridge messages can continue delivery while another payment is made. +`usdt_parse_payment_request` accepts raw addresses and chain-qualified ERC-681 requests for the pinned token, with exact atomic/scientific amounts. Ambiguous or unsupported parameters are rejected. The caller reviews the parsed amount before requesting a quote. -`balance()` returns USDT in millionths. `sync_history()` returns `true` when caught up and `false` when its 20-second work budget expires; request failures and invalid data return errors. Callers retain recovered activity and continue incomplete scans on a later refresh. Local activity has no fixed entry cap. +## Persistence and recovery -History scans bounded ranges from genesis, including deposits before delegation. Each completed page and partial-page receipt survives interruption; a completed scan revisits 4096 blocks for delayed indexing. Scans trail the reported tip by two blocks; this lag and revisit do not provide reorg rollback or retract previously saved orphaned activity. +Signed operations persist atomically before submission. Lost or rejected submission responses do not prove nonexecution: recovery retries only the identical signed operation. A quote ID cannot authorize a second payment. One source-chain payment remains pending at a time. -Incoming and outgoing transfers decode from token logs, including transfers made with another wallet. A combined incoming/EntryPoint filter and an outgoing filter require two log queries per range. Known timestamps are reused. +A matching operation event settles the payment. Expired signed paymaster terms and an unchanged confirmed EntryPoint nonce release an unmined operation; the shorter quote deadline does not. With an advanced nonce and missing indexed events, recovery checks every receipt in the consuming block. A matching event settles/replaces the payment; complete absence proves external nonce consumption. Missing receipts preserve the pending operation. Progress is stored by payment and block hash so interruption does not restart the proof or carry it onto another block. -Locally recorded payments are matched by their operation hash. For other payments, supported account calls recover payment and fee details only from transactions sent directly to the pinned EntryPoint. Their raw outgoing logs are excluded to avoid duplicates. Unknown wrappers and call shapes retain their token transfers without inventing payment or fee attribution. +Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls recover payment/fee attribution; unknown wrappers preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. -Range and response-size limits reduce the query span; rate limits return a distinct error. Pending-payment recovery can fall back to historical nonce reads and a single-block event query when its full log range is rejected. The RPC provider must support those historical state reads as well as full-history filtered logs. +`sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. -## Fees and infrastructure +Scans trail the reported tip by two blocks and revisit 4096 blocks for delayed indexing. This is not reorg rollback: previously recorded orphaned activity is not retracted. Providers must supply complete filtered logs, canonical blocks/receipts and historical state. -Only the exact Arbitrum USDT0 token is supported: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`, with six decimals. Amounts and fees are integers; no floating point or ETH balance is exposed to the user. +Payment outcomes and expiry decisions trust the configured chain RPC. A malicious RPC can fabricate or suppress evidence and mislead a user into authorizing another payment; these checks are not light-client proofs. -Pimlico's ERC20 paymaster is the initial provider. Its official supported-token list includes this token. The pinned EntryPoint 0.8 paymaster is `0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402`. +Storage is wallet-specific and owned by the `UsdtWallet` object. Drop it before deleting its database during an explicit wallet wipe. Async exports use UniFFI's Tokio adapter, preserving cancellation of the polled future; they do not detach sends onto the global runtime used by stateless exports. -A payment atomically batches a limited token approval and execution at the delegated address. Gas, token conversion, fixed charges and expiry are checked before the user approves the maximum fee. Final provider data, calldata and limits are signed together using the EntryPoint 0.8 EIP-712 hash, including the delegate. +## Transport and cross-network APIs -Local quotes last at most 120 seconds; newly quoted paymaster terms must have a finite deadline within 15 minutes. Recovery of a signed operation always follows its actual signed deadline. +Both chain and bundler endpoints must be controlled, credential-free HTTPS URLs; HTTP is accepted only on loopback for fixtures. Provider keys belong on the server. Chain/bundler calls share an 80/minute budget with a burst of 20. Responses are bounded to 2 MiB, except protocol-projected receipts up to 16 MiB. The companion service documents provider requirements, receipt projection and deployment limits. -The approval has a 5% margin, while the displayed maximum fee uses the signed gas limits and paymaster terms, not the allowance. Call gas and pre-verification gas receive a 10% margin for execution and Arbitrum L1 data-cost variation; provider verification estimates are retained. The pre-verification margin is charged as part of the payment fee and included in the displayed maximum. Changed or expired terms require another review. +`UsdtDepositClient` signs Orchestra deposit registration, history, detail and explicit refund requests for the derived account. It uses a separate optional service endpoint; estimates do not imply delivery. A clock-skew error requires correcting the device clock. Source-network fees are paid by the sender. Partner provisioning, delivered deposits and refund acceptance are separate release checks. -A residual allowance to the pinned paymaster can remain after collection; each subsequent payment sets a new finite allowance. The helper allowance is revoked in the bridge batch. +The outbound bridge API supports Ethereum (30101), Polygon (30109), Plasma (30383) and Stable (30396), alongside direct Arbitrum transfers. Native release flows expose Arbitrum only; bridge routes require explicit service enablement and destination acceptance. Plain deposits on another chain are not automatically forwarded. -Mainnet paymaster access requires a Pimlico project. Keep provider credentials on the server and validate the configured provider's pricing and policy before release. +Bridges use the pinned OFT and TransactionValueHelper with zero account ETH, a finite USDT approval covering principal/fee, and atomic helper-allowance revocation. The deployed helper requires native liquidity and retains behaviors noted in its OpenZeppelin audit; its verified runtime is not the audit-remediated implementation. Source success means bridging, not delivered. LayerZero status must match the operation GUID/pathway before confirmation; blocked delivery remains visible and never triggers an automatic paid retry. RPC providers see queried addresses; LayerZero Scan sees bridge transaction hashes. -Configure `USDT_BUNDLER_URL` with a controlled HTTPS endpoint forwarding the provider's bundler/paymaster JSON-RPC methods. Do not distribute a private provider key in the apps. A production proxy must retain the key, restrict chain/token/methods and apply abuse controls. Its provisioning belongs to the backend deployment, not to the mobile keychain. +## Validation and bindings -The apps intentionally show USDT as unavailable when this endpoint is missing. Both endpoints are required by the core constructor; there is no implicit public RPC. +Run `cargo test --locked --lib modules::usdt`; CI runs these deterministic tests. They cover independent signing/address vectors, fee bounds, uncertain submission, nonce recovery and restored history. Fixtures use public test credentials. -`USDT_RPC_URL` must point to the service's credential-free `/v1/usdt/chain-rpc` endpoint in both mobile apps. The private upstream URL is configured on the service as `ARBITRUM_RPC_URL`; it must never enter a mobile build. Rust allows plain HTTP only on localhost for fixtures. +For the ignored deployed-contract test, start a fresh Arbitrum Anvil fork on port 18545 and `tests/usdt-fork/provider.mjs` on 18546 after installing its pinned dependencies. Run `cargo test deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically -- --ignored`. The fixture requires Anvil, sets local balances/signing terms and checks deployed bytecode; it does not establish real provider pricing or destination delivery. -Chain and bundler calls share a budget of 80 requests per minute with an initial burst of 20. Native wallets poll every 30 seconds while idle and every ten seconds while receiving or awaiting a pending payment, with a shared ten-second minimum and one-minute backoff after rate limits. History scanning continues after a settlement error, but cancellation and throttling stop the refresh. +To include the service, start it with `USDT_BRIDGE_NETWORKS=ethereum,polygon,plasma,stable NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then pass `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` to the ignored test. -The standalone `bitkit-usdt-service` implements this endpoint at `/v1/usdt/rpc`. It runs independently of Blocktank, validates the pinned EIP-7702/USDT request shapes, and applies per-IP/global request and concurrency limits. Configure the apps with its full HTTPS route. - -It adds no wallet login or custody; the endpoint is public with constrained operations, not proof of official-app identity. Its README documents provider credentials, reverse-proxy configuration, limits and local testing. Provisioning and live-provider acceptance remain deployment prerequisites. - -- iOS reads these names from build settings/Info.plist or process configuration for local testing. -- Android reads these names through its existing local-properties/environment build configuration. -- Arbitrum One (42161) is the only supported USDT chain, including development builds. The UI always labels it. Bitcoin's network selection does not imply USDT testnet support. Storage remains under the existing Bitcoin-network/wallet directory, with a USDT-specific database and identity check. - -## Deposits and withdrawals - -Receive shares an ERC-681 token-transfer request identifying Arbitrum One and USDT0, and allows copying the raw account address. The sender must select Arbitrum One or bridge into it. - -`usdt_parse_payment_request()` accepts the pinned token-transfer URI with an optional `uint256` amount, including exact scientific notation in atomic units. Native send screens prefill that amount for editing and review. Duplicate or unsupported parameters are rejected. `quote_transfer()` takes a raw recipient address and an explicit amount after payment-request parsing and user review; it rejects URIs rather than discarding their requested amounts. - -Plain transfers to that address on Ethereum, TRON or another chain are not automatically forwarded. - -The optional Orchestra receive flow instead provides a separate reusable deposit address for each enabled source network. Ethereum and TRON USDT are supported by the adapter; the backend advertises only explicitly enabled, provider-supported routes into the pinned Arbitrum USDT0 account. The sender pays the source network fee, while routing costs are deducted from the deposited USDT. There is no Rhino or Relay integration. - -Configure `USDT_DEPOSITS_URL` with the service’s HTTPS `/v1/usdt/deposits` route and keep `ORCHESTRA_API_KEY` on the server. Wallet-signed requests bind deposit registration, status and explicit refund requests to the seed-derived address. Estimates are indicative; held deposits and refunds remain visible without implying settlement. - -A partner account, route acceptance, funded delivery and refund validation are release prerequisites. Without the endpoint, direct Arbitrum receive remains available. - -The native release exposes Arbitrum only. Bridge routes stay disabled at the service until destination delivery has passed funded acceptance. Same-chain payments transfer USDT directly. - -Cross-chain payments use the deployed USDT0 TransactionValueHelper, `0xa90f03c856D01F698E7071B393387cd75a8a319A`, and pinned Arbitrum OFT, `0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92`. The helper supplies native messaging value and collects USDT. Calls carry zero ETH from the account. The bridge approval covers principal plus a bounded token fee and is revoked atomically after sending. - -OFT amount/peer checks and helper balance/maxGas checks reject unavailable routes. The review screen shows the exact expected receipt and maximum additional fees. - -Supported destinations are Arbitrum One, Ethereum (30101), Polygon (30109), Plasma (30383), and Stable (30396). Arbitrum's LayerZero ID is 30110. TRON, Solana, TON and other deployments are not inferred from USDT0 branding; they are not offered. - -The helper is an operational dependency: its operator must maintain native liquidity and its oracle/markup affect pricing. Its verified deployed runtime (`0x9d4c3b4b79a3d31843ec75a03ce6ef43735421fdee489714519afc052f60ef27`) retains behaviors discussed in the OpenZeppelin audit; it is not the audit-remediated implementation. - -Bitkit constrains calls to zero native value, a pinned OFT, exact amounts, and finite approvals. Failed execution can still incur the authorized paymaster gas fee. - -Bridge-status polling shares a five-second refresh budget and rotates the first unresolved bridge checked, leaving source-chain recovery and sends available when delivery status is unavailable. - -A successful source receipt means the bridge has started. Bitkit matches the operation's GUID and LayerZero pathway before marking delivery confirmed. Failed, blocked or `PAYLOAD_STORED` delivery remains visible as requiring attention, with a tracking link. It does not silently retry a destination transaction or charge an additional recovery fee. - -The RPC sees queried wallet addresses, and LayerZero Scan sees bridge transaction hashes. Neither service holds the signing key. - -## Bindings and local builds - -Build core with the repository's `build_ios.sh` and `build_android.sh`, sequentially: the Android script temporarily changes the manifest and example source. Generated Swift/Kotlin bindings and native binaries must come from the same core source. - -For local iOS builds, `scripts/build-usdt-local.sh` in bitkit-ios takes the core directory followed by normal xcodebuild arguments and sets the `BITKIT_CORE_LOCAL` package override. Android accepts the absolute generated release AAR through the `bitkitCoreAar` Gradle property. - -## Validation - -The focused Rust suite covers independent address/signature parity, exact decimal amounts, chain-qualified QR requests, bounded paymaster and bridge fees, own-operation receipt attribution, uncertain submission, nonce/expiry recovery, and seed-restored history. The reference vector uses the public test mnemonic and ethers 6.17.0; it contains no production credential. - -The ignored deployed-contract test uses a fresh local Anvil fork of Arbitrum One on port 18545. Install the pinned test-only dependency in `tests/usdt-fork` and start its `provider.mjs` on port 18546, then run the ignored `deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically` Rust test. - -The fixture first requires an Anvil client, sets only local token balances and a local paymaster test signer, and uses the deployed contract bytecode. It exercises EIP-7702 activation, USDT post-operation collection, helper send/revoke, and helper failure. - -Its fixed estimates and test signatures do not validate Pimlico's real API policy, pricing or production credentials. Restart both fixture processes with a fresh fork for another run. - -To include the provider service in that same test, start it with `NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then set `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` when running the ignored test. These overrides affect only the fork test; normal wallet configuration uses `USDT_RPC_URL` and `USDT_BUNDLER_URL`. +Build iOS and Android sequentially with the repository scripts; Android temporarily edits the manifest/example. Generated bindings and native artifacts must use the same source. App configuration and local package overrides belong in each native repository's USDT documentation. ## References diff --git a/src/modules/usdt/deposits.rs b/src/modules/usdt/deposits.rs index 1ceea71..12c4114 100644 --- a/src/modules/usdt/deposits.rs +++ b/src/modules/usdt/deposits.rs @@ -1,12 +1,14 @@ use super::{ keys::{derive_key, key_address, parse_address}, + rpc::{bounded_json, endpoint_client}, + user_operation::sign_hash, UsdtError, }; -use alloy_primitives::{keccak256, Address}; -use bitcoin::secp256k1::{Message, Secp256k1}; +use alloy_primitives::{eip191_hash_message, Address}; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; use serde_json::{json, Value}; use std::{sync::Arc, time::Duration}; +use zeroize::Zeroizing; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] #[serde(rename_all = "lowercase")] @@ -79,24 +81,10 @@ pub struct UsdtDepositClient { impl UsdtDepositClient { #[uniffi::constructor] pub fn new(address: String, service_url: String) -> Result, UsdtError> { - let url = url::Url::parse(&service_url).map_err(|_| UsdtError::NotConfigured)?; - if (!matches!(url.scheme(), "https") - && !(url.scheme() == "http" - && matches!(url.host_str(), Some("localhost" | "127.0.0.1")))) - || !url.username().is_empty() - || url.password().is_some() - || url.query().is_some() - || url.fragment().is_some() - { - return Err(UsdtError::NotConfigured); - } + let client = endpoint_client(&service_url, Duration::from_secs(22))?; Ok(Arc::new(Self { address: parse_address(&address)?, - client: reqwest::Client::builder() - .timeout(Duration::from_secs(22)) - .redirect(reqwest::redirect::Policy::none()) - .build() - .map_err(|_| UsdtError::NetworkUnavailable)?, + client, url: service_url, })) } @@ -119,6 +107,8 @@ impl UsdtDepositClient { mnemonic: String, passphrase: Option, ) -> Result { + let mnemonic = Zeroizing::new(mnemonic); + let passphrase = passphrase.map(Zeroizing::new); if amount == 0 { return Err(UsdtError::InvalidAmount); } @@ -157,8 +147,8 @@ impl UsdtDepositClient { ) -> Result { self.call( json!({"action":"history","offset":offset}), - mnemonic, - passphrase, + mnemonic.into(), + passphrase.map(Into::into), ) .await } @@ -173,8 +163,8 @@ impl UsdtDepositClient { let result: UsdtDepositDetail = self .call( json!({"action":"detail","depositId":deposit_id,"offset":offset}), - mnemonic, - passphrase, + mnemonic.into(), + passphrase.map(Into::into), ) .await?; if result.deposit.id != deposit_id { @@ -192,6 +182,8 @@ impl UsdtDepositClient { mnemonic: String, passphrase: Option, ) -> Result<(), UsdtError> { + let mnemonic = Zeroizing::new(mnemonic); + let passphrase = passphrase.map(Zeroizing::new); validate_source_address(refund_address.trim(), network)?; let result: Value = self .call( @@ -212,8 +204,8 @@ impl UsdtDepositClient { async fn call( &self, payload: Value, - mnemonic: String, - passphrase: Option, + mnemonic: Zeroizing, + passphrase: Option>, ) -> Result { let signed = self.authorize(payload, mnemonic, passphrase, super::wallet::now())?; self.response(self.client.post(&self.url).json(&signed)) @@ -223,8 +215,8 @@ impl UsdtDepositClient { fn authorize( &self, payload: Value, - mnemonic: String, - passphrase: Option, + mnemonic: Zeroizing, + passphrase: Option>, timestamp: u64, ) -> Result { let key = derive_key(mnemonic, passphrase)?; @@ -235,14 +227,7 @@ impl UsdtDepositClient { json!({"owner":self.address.to_checksum(None),"timestamp":timestamp,"payload":payload}) .to_string(); let message = format!("Bitkit USDT deposits v1\n{request}"); - let hash = keccak256(format!( - "\x19Ethereum Signed Message:\n{}{message}", - message.len() - )); - let (recovery, signature) = Secp256k1::new() - .sign_ecdsa_recoverable(&Message::from_digest(hash.0), &key) - .serialize_compact(); - let signature = format!("0x{}{:02x}", hex::encode(signature), recovery.to_i32() + 27); + let signature = sign_hash(eip191_hash_message(message), &key)?; drop(key); Ok(json!({"request":request,"signature":signature})) } @@ -251,7 +236,7 @@ impl UsdtDepositClient { &self, request: reqwest::RequestBuilder, ) -> Result { - let mut response = request + let response = request .send() .await .map_err(|_| UsdtError::NetworkUnavailable)?; @@ -259,22 +244,13 @@ impl UsdtDepositClient { if status == reqwest::StatusCode::TOO_MANY_REQUESTS { return Err(UsdtError::RateLimited); } - let mut bytes = Vec::new(); - while let Some(chunk) = response - .chunk() - .await - .map_err(|_| UsdtError::NetworkUnavailable)? - { - if bytes.len() + chunk.len() > 262144 { - return Err(UsdtError::InvalidResponse); - } - bytes.extend_from_slice(&chunk); - } + let value = bounded_json(response, 262144, UsdtError::InvalidResponse).await; if !status.is_success() { - let error: Value = serde_json::from_slice(&bytes).unwrap_or_default(); + let error = value.unwrap_or_default(); return Err(match error["error"].as_str() { Some("not_configured") => UsdtError::NotConfigured, Some("invalid_authorization") => UsdtError::InvalidCredentials, + Some("clock_skew") => UsdtError::ClockSkew, Some("amount_too_small" | "amount_too_large" | "amount_exceeds_liquidity") => { UsdtError::InvalidAmount } @@ -290,7 +266,7 @@ impl UsdtDepositClient { _ => UsdtError::NetworkUnavailable, }); } - serde_json::from_slice(&bytes).map_err(Into::into) + serde_json::from_value(value?).map_err(Into::into) } } @@ -340,7 +316,7 @@ mod tests { client .authorize( request["payload"].clone(), - PHRASE.into(), + PHRASE.to_owned().into(), None, 1_800_000_000 ) @@ -350,8 +326,8 @@ mod tests { assert!(matches!( client.authorize( request["payload"].clone(), - PHRASE.into(), - Some("other".into()), + PHRASE.to_owned().into(), + Some("other".to_owned().into()), 1_800_000_000 ), Err(UsdtError::InvalidCredentials) diff --git a/src/modules/usdt/errors.rs b/src/modules/usdt/errors.rs index 2d9def1..8b15cc2 100644 --- a/src/modules/usdt/errors.rs +++ b/src/modules/usdt/errors.rs @@ -10,6 +10,8 @@ pub enum UsdtError { WrongNetwork, #[error("Wallet credentials do not match this USDT account")] InvalidCredentials, + #[error("Set your device date and time automatically, then try again")] + ClockSkew, #[error("This account uses another wallet's smart account. Restore its delegation before sending with Bitkit")] UnsupportedDelegation, #[error("The USDT balance does not cover the amount and maximum fee")] diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index 2bb7e2a..274ab58 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -10,32 +10,57 @@ use alloy_sol_types::{SolCall, SolEvent}; use serde_json::{json, Value}; use std::{collections::BTreeMap, sync::atomic::Ordering}; +pub(super) const MAX_LOG_RANGE: u64 = 10_000_000; + impl UsdtWallet { - pub(super) async fn scan_history(&self) -> Result<(), UsdtError> { + pub(super) async fn scan_history(&self) -> Result { + let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(20); let tip = self.block_number().await?.saturating_sub(2); let previous = self.store.synced_block()?; let start = self.store.history_progress()?.unwrap_or_else(|| { previous - .map(|newest| newest.saturating_sub(4096)) + .map(|block| block.saturating_sub(4096)) .unwrap_or(0) }); if start > tip { return Err(UsdtError::NetworkUnavailable); } let mut next = start; - let mut maximum_width = self.history_range_limit.load(Ordering::Relaxed); - let mut width = maximum_width.min(tip - start + 1); + let mut width = self + .history_range_limit + .load(Ordering::Relaxed) + .min(tip - start + 1); while next <= tip { + if tokio::time::Instant::now() >= deadline { + return Ok(false); + } let end = next.saturating_add(width - 1).min(tip); - match self.history_logs(next, end).await { + let query = tokio::time::timeout( + std::time::Duration::from_secs(10), + self.history_logs(next, end), + ) + .await; + let result = match query { + Ok(result) => result, + Err(_) => { + self.history_range_limit + .store((width / 2).max(1), Ordering::Relaxed); + return Ok(false); + } + }; + match result { Ok(transactions) => { - // Persist each receipt before advancing the range. An interrupted dense block - // resumes without re-fetching the receipts already saved in this scan. + if self.store.history_progress()? != Some(next) { + self.store.save_history_progress(next)?; + } let mut timestamps = BTreeMap::new(); for ((block, hash), logs) in transactions { if self.store.has_history_receipt(&hash)? { continue; } + if tokio::time::Instant::now() >= deadline { + return Ok(false); + } let needs_receipt = logs.iter().any(|log| { serde_json::from_value::
(log["address"].clone()) .is_ok_and(|address| address == ENTRY_POINT) @@ -61,26 +86,73 @@ impl UsdtWallet { timestamps.insert(block, timestamp); timestamp }; - let transfers = self.receipt_history(&hash, timestamp, &receipt).await?; - self.store.save_history_receipt(&transfers, &hash)?; + self.save_receipt_history(&hash, timestamp, &receipt) + .await?; } - if end == tip { - self.store.complete_history(tip)?; - break; - } - next = end + 1; - self.store.save_history_progress(next)?; - width = (width * 2).min(maximum_width).min(tip - next + 1); } Err(UsdtError::LogRangeTooLarge) if next < end => { width = (width / 2).max(1); - maximum_width = width; self.history_range_limit.store(width, Ordering::Relaxed); + continue; + } + Err(UsdtError::LogRangeTooLarge) => { + if !self.scan_block(next, deadline).await? { + return Ok(false); + } + } + Err(UsdtError::NetworkUnavailable) => { + self.history_range_limit + .store((width / 2).max(1), Ordering::Relaxed); + return Err(UsdtError::NetworkUnavailable); } Err(error) => return Err(error), } + if end == tip { + self.store.complete_history(tip)?; + return Ok(true); + } + next = end + 1; + self.store.save_history_progress(next)?; + width = (width * 2).min(MAX_LOG_RANGE); + self.history_range_limit.store(width, Ordering::Relaxed); + width = width.min(tip - next + 1); } - Ok(()) + Ok(true) + } + + async fn scan_block( + &self, + number: u64, + deadline: tokio::time::Instant, + ) -> Result { + let block = self.rpc.block(number).await?; + let timestamp = u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; + if self.store.history_progress()? != Some(number) { + self.store.save_history_progress(number)?; + } + for hash in &block.transactions { + let id = format!("{hash:#x}"); + if self.store.has_history_receipt(&id)? { + continue; + } + if tokio::time::Instant::now() >= deadline { + return Ok(false); + } + let receipt = self.rpc.block_receipt(*hash, &block, number).await?; + self.save_receipt_history(&id, timestamp, &receipt).await?; + } + Ok(true) + } + + async fn save_receipt_history( + &self, + hash: &str, + timestamp: u64, + receipt: &Value, + ) -> Result<(), UsdtError> { + // Finish and persist a receipt before yielding the work budget. + let transfers = self.receipt_history(hash, timestamp, receipt).await?; + self.store.save_history_receipt(&transfers, hash) } async fn history_logs( @@ -102,6 +174,13 @@ impl UsdtWallet { if log["removed"].as_bool() == Some(true) { continue; } + if serde_json::from_value::
(log["address"].clone())? == TOKEN { + let event = Erc20::Transfer::decode_log_data(&event_data(&log)?) + .map_err(|_| UsdtError::InvalidResponse)?; + if event.value.is_zero() || event.from == event.to { + continue; + } + } let hash: String = serde_json::from_value(log["transactionHash"].clone())?; let block = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) .map_err(|_| UsdtError::InvalidResponse)?; @@ -159,7 +238,6 @@ impl UsdtWallet { destination: UsdtDestination::Arbitrum, amount: token_amount(event.value)?, received_amount: token_amount(event.value)?, - bridge_fee: 0, fee: None, is_incoming: incoming, status: UsdtTransferStatus::Confirmed, @@ -213,7 +291,6 @@ impl UsdtWallet { destination, amount, received_amount: amount, - bridge_fee: 0, fee: None, is_incoming: false, status: UsdtTransferStatus::Pending, diff --git a/src/modules/usdt/keys.rs b/src/modules/usdt/keys.rs index 94f62f8..f338575 100644 --- a/src/modules/usdt/keys.rs +++ b/src/modules/usdt/keys.rs @@ -1,5 +1,5 @@ use super::UsdtError; -use alloy_primitives::{keccak256, Address}; +use alloy_primitives::Address; use bip39::Mnemonic; use bitcoin::{ bip32::{DerivationPath, Xpriv}, @@ -22,12 +22,11 @@ impl Drop for SigningKey { } pub(super) fn derive_key( - mnemonic: String, - passphrase: Option, + mnemonic: Zeroizing, + passphrase: Option>, ) -> Result { - let phrase = Zeroizing::new(mnemonic); - let passphrase = Zeroizing::new(passphrase.unwrap_or_default()); - let mnemonic = Mnemonic::parse(&*phrase).map_err(|_| UsdtError::InvalidCredentials)?; + let passphrase = passphrase.unwrap_or_default(); + let mnemonic = Mnemonic::parse(&*mnemonic).map_err(|_| UsdtError::InvalidCredentials)?; let seed = Zeroizing::new(mnemonic.to_seed(passphrase.as_str())); let mut root = Xpriv::new_master(bitcoin::Network::Bitcoin, seed.as_ref()) .map_err(|_| UsdtError::InvalidCredentials)?; @@ -35,21 +34,20 @@ pub(super) fn derive_key( DerivationPath::from_str("m/44'/60'/0'/0/0").map_err(|_| UsdtError::InvalidCredentials)?; let derived = root.derive_priv(&Secp256k1::new(), &path); root.private_key.non_secure_erase(); - Ok(SigningKey( - derived - .map_err(|_| UsdtError::InvalidCredentials)? - .private_key, - )) + let mut derived = derived.map_err(|_| UsdtError::InvalidCredentials)?; + let key = SigningKey(derived.private_key); + derived.private_key.non_secure_erase(); + Ok(key) } pub(super) fn key_address(key: &SecretKey) -> Address { let public = key.public_key(&Secp256k1::new()).serialize_uncompressed(); - Address::from_slice(&keccak256(&public[1..])[12..]) + Address::from_raw_public_key(&public[1..]) } #[uniffi::export] pub fn usdt_address(mnemonic: String, passphrase: Option) -> Result { - Ok(key_address(&*derive_key(mnemonic, passphrase)?).to_checksum(None)) + Ok(key_address(&*derive_key(mnemonic.into(), passphrase.map(Into::into))?).to_checksum(None)) } pub(super) fn parse_address(value: &str) -> Result { diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs index bbe90cf..528af37 100644 --- a/src/modules/usdt/paymaster.rs +++ b/src/modules/usdt/paymaster.rs @@ -13,6 +13,13 @@ use serde_json::json; use super::types::{CHAIN_ID as ARBITRUM_CHAIN_ID, TOKEN as USDT0}; pub(super) const PAYMASTER: Address = address!("888888888888Ec68A58AB8094Cc1AD20Ba3D2402"); +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GasPrice { + max_fee_per_gas: U256, + max_priority_fee_per_gas: U256, +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct TokenQuote { @@ -95,16 +102,6 @@ impl Pimlico { struct Quotes { quotes: Vec, } - #[derive(Deserialize)] - struct Prices { - fast: GasPrice, - } - #[derive(Deserialize)] - #[serde(rename_all = "camelCase")] - struct GasPrice { - max_fee_per_gas: U256, - max_priority_fee_per_gas: U256, - } let quotes: Quotes = self .rpc .call( @@ -121,15 +118,7 @@ impl Pimlico { && !quote.exchange_rate.is_zero() }) .ok_or(UsdtError::UnsupportedRoute)?; - let price: Prices = self - .rpc - .call("pimlico_getUserOperationGasPrice", json!([])) - .await?; - if price.fast.max_fee_per_gas.is_zero() - || price.fast.max_priority_fee_per_gas > price.fast.max_fee_per_gas - { - return Err(UsdtError::InvalidResponse); - } + let price = self.gas_price().await?; let dummy_signature = Bytes::from_static(&alloy_primitives::hex!("fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c")); let mut op = UserOperation { sender: address, @@ -140,8 +129,8 @@ impl Pimlico { call_gas_limit: U256::ZERO, verification_gas_limit: U256::ZERO, pre_verification_gas: U256::ZERO, - max_fee_per_gas: price.fast.max_fee_per_gas, - max_priority_fee_per_gas: price.fast.max_priority_fee_per_gas, + max_fee_per_gas: price.max_fee_per_gas, + max_priority_fee_per_gas: price.max_priority_fee_per_gas, paymaster: PAYMASTER, paymaster_verification_gas_limit: U256::ZERO, paymaster_post_op_gas_limit: U256::ZERO, @@ -164,7 +153,7 @@ impl Pimlico { valid_after: 0, }; let mut allowance = approval_margin(estimate_terms.maximum_token_cost(&op)?)?; - // Final provider data can include a fixed token charge. Rebuild the bounded approval before signing. + // Provider data can change gas limits and token charges. Refine both before signing. for _ in 0..3 { op.call_data = with_approval(calls, allowance); self.apply_paymaster(&mut op, "pm_getPaymasterData").await?; @@ -220,6 +209,12 @@ impl Pimlico { } pub async fn validate_gas(&self, op: &UserOperation) -> Result<(), UsdtError> { + let price = self.gas_price().await?; + if price.max_fee_per_gas > op.max_fee_per_gas + || price.max_priority_fee_per_gas > op.max_priority_fee_per_gas + { + return Err(UsdtError::QuoteExpired); + } let estimate: GasEstimate = self .rpc .call("eth_estimateUserOperationGas", json!([op, ENTRY_POINT])) @@ -238,6 +233,23 @@ impl Pimlico { } Ok(()) } + + async fn gas_price(&self) -> Result { + #[derive(Deserialize)] + struct Prices { + fast: GasPrice, + } + let price: Prices = self + .rpc + .call("pimlico_getUserOperationGasPrice", json!([])) + .await?; + if price.fast.max_fee_per_gas.is_zero() + || price.fast.max_priority_fee_per_gas > price.fast.max_fee_per_gas + { + return Err(UsdtError::InvalidResponse); + } + Ok(price.fast) + } } fn with_approval(calls: &[(Address, Bytes)], amount: U256) -> Bytes { diff --git a/src/modules/usdt/payment_request.rs b/src/modules/usdt/payment_request.rs index 89d2fba..d28a174 100644 --- a/src/modules/usdt/payment_request.rs +++ b/src/modules/usdt/payment_request.rs @@ -19,9 +19,12 @@ fn parse_request(value: &str) -> Result<(Address, Option), UsdtError> { if value.len() > 2048 { return Err(UsdtError::InvalidAddress); } - let Some(uri) = value.strip_prefix("ethereum:") else { + let Some((scheme, uri)) = value.split_once(':') else { return Ok((parse_address(value)?, None)); }; + if !scheme.eq_ignore_ascii_case("ethereum") { + return Err(UsdtError::InvalidAddress); + } let (target, query) = uri.split_once('?').unwrap_or((uri, "")); let (address, chain) = target .strip_prefix("pay-") diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs index f43522e..35a3cee 100644 --- a/src/modules/usdt/rpc.rs +++ b/src/modules/usdt/rpc.rs @@ -1,5 +1,5 @@ use super::{UsdtError, UsdtTransfer, UsdtTransferStatus}; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_sol_types::SolCall; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::{json, Value}; @@ -9,6 +9,13 @@ use tokio::{sync::Mutex, time::Instant}; const REQUEST_INTERVAL: Duration = Duration::from_millis(750); const BURST_WINDOW: Duration = Duration::from_millis(750 * 19); +#[derive(Deserialize)] +pub(super) struct Block { + pub hash: B256, + pub timestamp: U256, + pub transactions: Vec, +} + pub(super) struct Rpc { client: reqwest::Client, #[cfg(test)] @@ -31,18 +38,7 @@ struct RpcError { impl Rpc { pub fn new(url: String, chain_id: u64) -> Result { - let parsed = url::Url::parse(&url).map_err(|_| UsdtError::NetworkUnavailable)?; - if parsed.scheme() != "https" - && !(parsed.scheme() == "http" - && matches!(parsed.host_str(), Some("127.0.0.1" | "localhost"))) - { - return Err(UsdtError::NetworkUnavailable); - } - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(25)) - .redirect(reqwest::redirect::Policy::none()) - .build() - .map_err(|_| UsdtError::NetworkUnavailable)?; + let client = endpoint_client(&url, Duration::from_secs(25))?; Ok(Self { client, #[cfg(test)] @@ -83,16 +79,25 @@ impl Rpc { if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS { return Err(UsdtError::RateLimited); } - let response = response - .error_for_status() - .map_err(|_| UsdtError::NetworkUnavailable)?; + let status = response.status(); let overflow = if method == "eth_getLogs" { UsdtError::LogRangeTooLarge } else { UsdtError::InvalidResponse }; + // The proxy projects receipts to fixed-size USDT protocol events. + let limit = if method == "eth_getTransactionReceipt" { + 16 * 1024 * 1024 + } else { + 2_097_152 + }; + let body = bounded_json(response, limit, overflow).await; let response: Response = - serde_json::from_value(Self::bounded_json(response, overflow).await?)?; + match body.and_then(|value| serde_json::from_value(value).map_err(Into::into)) { + Ok(response) => response, + Err(_) if !status.is_success() => return Err(UsdtError::NetworkUnavailable), + Err(error) => return Err(error), + }; if let Some(error) = response.error { let message = error.message.to_ascii_lowercase(); if error.code == -32016 @@ -112,6 +117,9 @@ impl Rpc { if method == "eth_getLogs" && error.code == -32005 { return Err(UsdtError::LogRangeTooLarge); } + if error.code == -32002 { + return Err(UsdtError::NetworkUnavailable); + } if message.contains("insufficient funds") || message.contains("insufficient balance") { return Err(UsdtError::InsufficientBalance); } @@ -119,6 +127,9 @@ impl Rpc { reason: error.message.chars().take(200).collect(), }); } + if !status.is_success() { + return Err(UsdtError::NetworkUnavailable); + } serde_json::from_value(response.result.unwrap_or(Value::Null)).map_err(Into::into) } @@ -155,7 +166,7 @@ impl Rpc { .map_err(|_| UsdtError::NetworkUnavailable)? .error_for_status() .map_err(|_| UsdtError::NetworkUnavailable)?; - let response = Self::bounded_json(response, UsdtError::InvalidResponse).await?; + let response = bounded_json(response, 2_097_152, UsdtError::InvalidResponse).await?; let messages = response["data"] .as_array() .ok_or(UsdtError::InvalidResponse)?; @@ -188,29 +199,34 @@ impl Rpc { }) } - async fn bounded_json( - mut response: reqwest::Response, - overflow: UsdtError, - ) -> Result { - let mut body = Vec::new(); - while let Some(chunk) = response - .chunk() + pub async fn balance(&self, address: Address) -> Result { + self.call("eth_getBalance", json!([address, "pending"])) .await - .map_err(|_| UsdtError::NetworkUnavailable)? - { - if body.len() + chunk.len() > 2_097_152 { - return Err(overflow); - } - body.extend_from_slice(&chunk); - } - serde_json::from_slice(&body).map_err(Into::into) } - pub async fn balance(&self, address: Address) -> Result { - self.call("eth_getBalance", json!([address, "pending"])) + pub async fn block(&self, number: u64) -> Result { + self.call("eth_getBlockByNumber", json!([U256::from(number), false])) .await } + pub async fn block_receipt( + &self, + hash: B256, + block: &Block, + number: u64, + ) -> Result { + let receipt: Value = self + .call("eth_getTransactionReceipt", json!([hash])) + .await?; + if serde_json::from_value::(receipt["transactionHash"].clone())? != hash + || serde_json::from_value::(receipt["blockHash"].clone())? != block.hash + || serde_json::from_value::(receipt["blockNumber"].clone())? != U256::from(number) + { + return Err(UsdtError::InvalidResponse); + } + Ok(receipt) + } + pub async fn contract(&self, to: Address, call: C) -> Result { let bytes: Bytes = self .call( @@ -222,10 +238,103 @@ impl Rpc { } } +pub(super) fn endpoint_client(url: &str, timeout: Duration) -> Result { + let parsed = url::Url::parse(url).map_err(|_| UsdtError::NotConfigured)?; + if (parsed.scheme() != "https" + && !(parsed.scheme() == "http" + && matches!(parsed.host_str(), Some("127.0.0.1" | "localhost")))) + || !parsed.username().is_empty() + || parsed.password().is_some() + || parsed.query().is_some() + || parsed.fragment().is_some() + { + return Err(UsdtError::NotConfigured); + } + reqwest::Client::builder() + .timeout(timeout) + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(|_| UsdtError::NetworkUnavailable) +} + +pub(super) async fn bounded_json( + mut response: reqwest::Response, + limit: usize, + overflow: UsdtError, +) -> Result { + let mut body = Vec::new(); + while let Some(chunk) = response + .chunk() + .await + .map_err(|_| UsdtError::NetworkUnavailable)? + { + if body.len() + chunk.len() > limit { + return Err(overflow); + } + body.extend_from_slice(&chunk); + } + serde_json::from_slice(&body).map_err(Into::into) +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn endpoints_reject_embedded_credentials_and_insecure_remote_hosts() { + for url in [ + "http://example.com", + "https://user:key@example.com", + "https://example.com?apikey=secret", + "https://example.com#secret", + "not a url", + ] { + assert!(matches!( + Rpc::new(url.into(), 42161), + Err(UsdtError::NotConfigured) + )); + } + assert!(Rpc::new("https://example.com/v1/usdt/chain-rpc".into(), 42161).is_ok()); + assert!(Rpc::new("http://127.0.0.1:3100/v1/usdt/chain-rpc".into(), 42161).is_ok()); + } + + #[tokio::test] + async fn http_errors_preserve_structured_rejections_and_network_failures() { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + for (status, body) in [ + ( + 400, + r#"{"error":{"code":-32602,"message":"Unsupported USDT request"}}"#, + ), + ( + 502, + r#"{"error":{"code":-32002,"message":"Provider unavailable"}}"#, + ), + (503, "Service unavailable"), + ] { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let url = format!("http://{}", listener.local_addr().unwrap()); + let server = tokio::spawn(async move { + let (mut socket, _) = listener.accept().await.unwrap(); + let mut request = [0u8; 4096]; + assert!(socket.read(&mut request).await.unwrap() > 0); + let response = format!("HTTP/1.1 {status} Error\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", body.len()); + socket.write_all(response.as_bytes()).await.unwrap(); + }); + let error = Rpc::new(url, 42161) + .unwrap() + .call::("eth_estimateUserOperationGas", json!([])) + .await + .unwrap_err(); + if status == 400 { + assert!(matches!(error, UsdtError::TransactionRejected { .. })); + } else { + assert!(matches!(error, UsdtError::NetworkUnavailable)); + } + server.await.unwrap(); + } + } + #[tokio::test(start_paused = true)] async fn chain_and_bundler_share_bursts_and_sustained_budget() { let chain = Rpc::new("https://chain.example".into(), 42161).unwrap(); diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs index 9c5d052..a124d32 100644 --- a/src/modules/usdt/store.rs +++ b/src/modules/usdt/store.rs @@ -29,6 +29,7 @@ impl Store { CREATE TABLE IF NOT EXISTS usdt_history_progress (id INTEGER PRIMARY KEY CHECK(id=1), next INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_history_receipts (hash TEXT PRIMARY KEY); CREATE TABLE IF NOT EXISTS usdt_quotes (id TEXT PRIMARY KEY, data TEXT NOT NULL); + CREATE TABLE IF NOT EXISTS usdt_nonce_recovery (id TEXT PRIMARY KEY, block_hash TEXT NOT NULL, next_transaction INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_transfers (id TEXT PRIMARY KEY, hash TEXT NOT NULL, raw TEXT, data TEXT NOT NULL);")?; connection.execute( "INSERT OR IGNORE INTO usdt_identity VALUES (1,?1)", @@ -137,8 +138,42 @@ impl Store { | UsdtTransferStatus::Bridging | UsdtTransferStatus::BridgeNeedsAttention ); - self.connection()?.execute("UPDATE usdt_transfers SET data=?1, raw=CASE WHEN ?2 THEN NULL ELSE raw END WHERE id=?3", + let mut connection = self.connection()?; + let tx = connection.transaction()?; + tx.execute("UPDATE usdt_transfers SET data=?1, raw=CASE WHEN ?2 THEN NULL ELSE raw END WHERE id=?3", params![serde_json::to_string(transfer)?, settled, transfer.id])?; + if settled { + tx.execute( + "DELETE FROM usdt_nonce_recovery WHERE id=?1", + [&transfer.id], + )?; + } + tx.commit()?; + Ok(()) + } + + pub fn nonce_recovery(&self, id: &str, block_hash: &str) -> Result { + Ok(self + .connection()? + .query_row( + "SELECT next_transaction FROM usdt_nonce_recovery WHERE id=?1 AND block_hash=?2", + params![id, block_hash], + |row| row.get(0), + ) + .optional()? + .unwrap_or(0)) + } + + pub fn save_nonce_recovery( + &self, + id: &str, + block_hash: &str, + next_transaction: usize, + ) -> Result<(), UsdtError> { + self.connection()?.execute( + "INSERT INTO usdt_nonce_recovery VALUES (?1,?2,?3) ON CONFLICT(id) DO UPDATE SET block_hash=excluded.block_hash,next_transaction=excluded.next_transaction", + params![id, block_hash, next_transaction], + )?; Ok(()) } @@ -243,6 +278,10 @@ impl Store { "UPDATE usdt_transfers SET data=?1, raw=NULL WHERE id=?2", params![serde_json::to_string(&transfer)?, transfer.id], )?; + tx.execute( + "DELETE FROM usdt_nonce_recovery WHERE id=?1", + [&transfer.id], + )?; } else { tx.execute( "INSERT INTO usdt_transfers (id,hash,data) VALUES (?1,?2,?3)", diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index df74c39..d4bc3f5 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -38,7 +38,7 @@ fn account_and_signatures_match_entrypoint_v8_reference() { .parse::() .unwrap() ); - let key = keys::derive_key(TEST_PHRASE.into(), None).unwrap(); + let key = keys::derive_key(TEST_PHRASE.to_owned().into(), None).unwrap(); assert_eq!(op.sign(&key, 42161).unwrap(), expected_hash); assert_eq!( op.signature, @@ -219,6 +219,7 @@ struct ChainState { authorization_nonce: u64, paymaster_valid_until: Option, pre_verification_estimates: std::collections::VecDeque, + gas_price: u64, external_outgoing: bool, nonce: u64, balance: alloy_primitives::U256, @@ -235,6 +236,12 @@ struct ChainState { receipt_logs: Option>, incoming: bool, hide_logs: bool, + hide_receipts: bool, + receipt_failure: Option, + oversized_block: Option, + receipt_padding: usize, + log_response: Option>, + block_transactions: Option>, replacement_block: Option, log_error: Option<(i64, String)>, max_log_range: Option, @@ -261,6 +268,7 @@ impl MockChain { authorization_nonce: 0, paymaster_valid_until: None, pre_verification_estimates: Default::default(), + gas_price: 50_000_000, external_outgoing: false, nonce: 0, balance: U256::from(10_000_000), @@ -277,6 +285,12 @@ impl MockChain { receipt_logs: None, incoming: false, hide_logs: false, + hide_receipts: false, + receipt_failure: None, + oversized_block: None, + receipt_padding: 0, + log_response: None, + block_transactions: None, replacement_block: None, log_error: None, max_log_range: None, @@ -373,7 +387,7 @@ impl ChainState { self.fail_block_read_at = None; return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"temporarily unavailable"}}); } - json!({"timestamp":self.timestamp}) + json!({"hash":alloy_primitives::B256::repeat_byte(9),"timestamp":self.timestamp,"transactions":self.block_transactions.clone().unwrap_or_else(|| vec![alloy_primitives::B256::repeat_byte(7)])}) } "eth_getCode" => json!(self.account_code), "eth_getTransactionCount" => json!(U256::from(self.authorization_nonce)), @@ -435,7 +449,7 @@ impl ChainState { json!({"quotes":[{"token":types::TOKEN,"paymaster":self.paymaster,"postOpGas":"0xc350","exchangeRate":U256::from(3_000_000_000u64)}]}) } "pimlico_getUserOperationGasPrice" => { - json!({"fast":{"maxFeePerGas":U256::from(50_000_000),"maxPriorityFeePerGas":U256::from(100000)}}) + json!({"fast":{"maxFeePerGas":U256::from(self.gas_price),"maxPriorityFeePerGas":U256::from(100000)}}) } "pm_getPaymasterData" | "pm_getPaymasterStubData" => { let mut data = vec![0; 118]; @@ -491,6 +505,15 @@ impl ChainState { let filter = &body["params"][0]; let from: U256 = serde_json::from_value(filter["fromBlock"].clone()).unwrap(); let to: U256 = serde_json::from_value(filter["toBlock"].clone()).unwrap(); + if self + .oversized_block + .is_some_and(|block| from <= U256::from(block) && to >= U256::from(block)) + { + return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32005,"message":"Log query limit exceeded"}}); + } + if let Some(logs) = &self.log_response { + return json!({"jsonrpc":"2.0","id":1,"result":logs}); + } if self .max_log_range .is_some_and(|limit| to - from + U256::from(1) > U256::from(limit)) @@ -559,7 +582,14 @@ impl ChainState { } } "eth_getTransactionReceipt" => { - json!({"logs":self.receipt_logs.clone().unwrap_or_else(|| self.event_logs())}) + if self.hide_receipts + || self + .receipt_failure + .is_some_and(|hash| body["params"][0] == json!(hash)) + { + return json!({"jsonrpc":"2.0","id":1,"result":null}); + } + json!({"transactionHash":body["params"][0],"blockHash":alloy_primitives::B256::repeat_byte(9),"blockNumber":"0x4e20","logs":self.receipt_logs.clone().unwrap_or_else(|| self.event_logs()),"padding":" ".repeat(self.receipt_padding)}) } "eth_getTransactionByHash" => { let op = &self.operations[0]; @@ -885,7 +915,6 @@ async fn bridge_payment_bounds_token_fees_and_revokes_helper_approval() { let paymaster = transaction::Erc20::approveCall::abi_decode(&calls[0].1).unwrap(); assert_eq!(calls[0].0, types::TOKEN); assert_eq!(paymaster.spender, paymaster::PAYMASTER); - assert!(paymaster.amount > U256::from(quote.maximum_fee - plan.bridge_fee)); let approval = transaction::Erc20::approveCall::abi_decode(&calls[1].1).unwrap(); assert_eq!(calls[1].0, types::TOKEN); assert_eq!(approval.spender, types::BRIDGE_HELPER); @@ -906,7 +935,9 @@ async fn bridge_payment_bounds_token_fees_and_revokes_helper_approval() { let revoke = transaction::Erc20::approveCall::abi_decode(&calls[3].1).unwrap(); assert_eq!(revoke.spender, types::BRIDGE_HELPER); assert!(revoke.amount.is_zero()); - assert_eq!(plan.bridge_fee, 360_001); + let bridge_fee = approval.amount.to::() - quote.amount; + assert_eq!(bridge_fee, 360_001); + assert!(paymaster.amount > U256::from(quote.maximum_fee - bridge_fee)); assert_eq!(quote.received_amount, 1_000_000); chain.state.lock().unwrap().helper_balance = U256::ZERO; assert!(matches!( @@ -997,11 +1028,20 @@ async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_ wallet.settle(&mut transfer, &receipt, false).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Failed); assert_eq!(transfer.fee, Some(123)); - assert_eq!(transfer.bridge_fee, 0); assert_eq!(transfer.bridge_guid, None); wallet.settle(&mut transfer, &receipt, true).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::BridgeNeedsAttention); assert_eq!(transfer.bridge_guid, None); + assert_eq!(transfer.fee, Some(123)); + + let mut receipt = receipt; + let bridge_log = receipt["logs"][1].clone(); + receipt["logs"] + .as_array_mut() + .unwrap() + .insert(4, bridge_log); + wallet.settle(&mut transfer, &receipt, true).unwrap(); + assert_eq!(transfer.fee, Some(623)); } #[tokio::test] @@ -1059,7 +1099,6 @@ async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomical let history = wallet.refresh_transfers().await.unwrap(); let failed = history.iter().find(|t| t.id == sent.id).unwrap(); assert_eq!(failed.status, UsdtTransferStatus::Failed); - assert_eq!(failed.bridge_fee, 0); assert!(failed.bridge_guid.is_none()); assert!(before - wallet.balance().await.unwrap() <= bridge.maximum_fee); let _: serde_json::Value = rpc @@ -1083,7 +1122,7 @@ async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomical let bridged = history.iter().find(|t| t.id == sent.id).unwrap(); assert_eq!(bridged.status, UsdtTransferStatus::Bridging); assert!(bridged.bridge_guid.is_some()); - assert!(bridged.bridge_fee > 0); + assert!(bridged.fee.unwrap() > 0); assert!(bridged.fee.unwrap() <= bridge.maximum_fee); sync_history_to_tip(&wallet).await; assert_eq!( @@ -1278,7 +1317,6 @@ fn stored_activity_is_complete_and_sorted_newest_first() { destination: UsdtDestination::Arbitrum, amount: 1, received_amount: 1, - bridge_fee: 0, fee: None, is_incoming: true, status: UsdtTransferStatus::Confirmed, @@ -1323,13 +1361,15 @@ async fn nonce_advance_with_delayed_logs_remains_pending_and_history_reconciles( let mut state = chain.state.lock().unwrap(); state.mined = true; state.hide_logs = true; + state.hide_receipts = true; state.max_log_range = Some(1); state.nonce = 1; state.tip += 3; state.timestamp += alloy_primitives::U256::from(180); } + assert!(wallet.refresh_transfers().await.is_err()); assert_eq!( - wallet.refresh_transfers().await.unwrap()[0].status, + wallet.history().unwrap()[0].status, UsdtTransferStatus::Pending ); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); @@ -1342,6 +1382,7 @@ async fn nonce_advance_with_delayed_logs_remains_pending_and_history_reconciles( { let mut state = chain.state.lock().unwrap(); state.hide_logs = false; + state.hide_receipts = false; state.max_log_range = None; } sync_history_to_tip(&wallet).await; @@ -1427,11 +1468,13 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { state.tip = 508_000_000; state.replacement_block = Some(507_000_000); state.hide_logs = true; + state.hide_receipts = true; state.max_log_range = Some(1); } let wallet = chain.wallet(&dir); + assert!(wallet.refresh_transfers().await.is_err()); assert_eq!( - wallet.refresh_transfers().await.unwrap()[0].status, + wallet.history().unwrap()[0].status, UsdtTransferStatus::Pending ); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); @@ -1499,6 +1542,17 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { } sync_history_to_tip(&wallet).await; assert_eq!(wallet.store.synced_block().unwrap(), Some(19_998)); + chain.state.lock().unwrap().max_log_range = None; + wallet + .history_range_limit + .store(1, std::sync::atomic::Ordering::Relaxed); + sync_history_to_tip(&wallet).await; + assert!( + wallet + .history_range_limit + .load(std::sync::atomic::Ordering::Relaxed) + > 1 + ); } #[tokio::test] @@ -1657,7 +1711,6 @@ async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending destination: UsdtDestination::Polygon, amount: 1_000_000, received_amount: 1_000_000, - bridge_fee: 10, fee: Some(20), is_incoming: false, status: UsdtTransferStatus::Bridging, @@ -1965,3 +2018,162 @@ async fn seed_restore_includes_external_token_sends_without_duplicate_operation_ assert_eq!(restored.history().unwrap().len(), 1); } } + +#[tokio::test] +async fn gas_price_changes_require_a_new_quote_before_signing() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + chain.state.lock().unwrap().gas_price = 60_000_000; + assert!(matches!( + wallet.send(quote.id, TEST_PHRASE.into(), None).await, + Err(UsdtError::QuoteExpired) + )); + assert!(wallet.history().unwrap().is_empty()); + assert!(chain.state.lock().unwrap().operations.is_empty()); +} + +#[tokio::test] +async fn consumed_nonce_recovery_requires_complete_receipts_and_resumes_after_restart() { + use alloy_primitives::B256; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 1; + state.tip += 3; + state.hide_logs = true; + state.block_transactions = Some(vec![B256::repeat_byte(6), B256::repeat_byte(7)]); + state.receipt_failure = Some(B256::repeat_byte(7)); + } + assert!(wallet.refresh_transfers().await.is_err()); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + let block_hash = format!("{:#x}", B256::repeat_byte(9)); + assert_eq!( + wallet.store.nonce_recovery(&sent.id, &block_hash).unwrap(), + 1 + ); + drop(wallet); + chain.state.lock().unwrap().receipt_failure = None; + let restored = chain.wallet(&dir); + let history = restored.refresh_transfers().await.unwrap(); + assert_eq!(history[0].status, UsdtTransferStatus::Replaced); + assert_eq!(history[0].fee, Some(0)); + assert_eq!(history[0].received_amount, 0); + assert!(restored.store.pending_plan(&sent.id).unwrap().is_none()); + assert_eq!( + restored + .store + .nonce_recovery(&sent.id, &block_hash) + .unwrap(), + 0 + ); + restored + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); +} + +#[tokio::test] +async fn consuming_block_receipts_recover_a_payment_hidden_from_log_queries() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 1; + state.tip += 3; + state.hide_logs = true; + state.mined = true; + } + let history = wallet.refresh_transfers().await.unwrap(); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); +} + +#[tokio::test] +async fn dense_block_history_recovers_large_receipts_without_skipping_after_restart() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + state.oversized_block = Some(20000); + state.receipt_padding = 3 * 1024 * 1024; + state.hide_receipts = true; + } + while let Ok(complete) = wallet.sync_history().await { + assert!(!complete); + } + assert_eq!(wallet.store.history_progress().unwrap(), Some(20000)); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + drop(wallet); + chain.state.lock().unwrap().hide_receipts = false; + let restored = chain.wallet(&dir); + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 1); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); + assert_eq!(restored.store.synced_block().unwrap(), Some(20001)); +} + +#[tokio::test] +async fn zero_value_transfers_do_not_require_receipts_or_timestamps() { + use alloy_primitives::{B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let event = transaction::Erc20::Transfer { + from: wallet.address, + to: RECIPIENT.parse().unwrap(), + value: U256::ZERO, + } + .encode_log_data(); + { + let mut state = chain.state.lock().unwrap(); + state.log_response = Some(vec![ + json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x1","logIndex":"0x0"}), + ]); + state.hide_receipts = true; + state.fail_block_read_at = Some(1); + } + assert!(wallet.sync_history().await.unwrap()); + assert!(wallet.history().unwrap().is_empty()); +} diff --git a/src/modules/usdt/transaction.rs b/src/modules/usdt/transaction.rs index b4ec33e..4d7c8d6 100644 --- a/src/modules/usdt/transaction.rs +++ b/src/modules/usdt/transaction.rs @@ -5,14 +5,44 @@ use bitcoin::secp256k1::SecretKey; use serde::{Deserialize, Serialize}; sol! { - struct PackedOperation { address sender; uint256 nonce; bytes initCode; bytes callData; bytes32 accountGasLimits; uint256 preVerificationGas; bytes32 gasFees; bytes paymasterAndData; bytes signature; } + struct PackedOperation { + address sender; + uint256 nonce; + bytes initCode; + bytes callData; + bytes32 accountGasLimits; + uint256 preVerificationGas; + bytes32 gasFees; + bytes paymasterAndData; + bytes signature; + } #[derive(Debug)] - struct SendParam { uint32 dstEid; bytes32 to; uint256 amountLD; uint256 minAmountLD; bytes extraOptions; bytes composeMsg; bytes oftCmd; } + struct SendParam { + uint32 dstEid; + bytes32 to; + uint256 amountLD; + uint256 minAmountLD; + bytes extraOptions; + bytes composeMsg; + bytes oftCmd; + } #[derive(Debug)] - struct MessagingFee { uint256 nativeFee; uint256 lzTokenFee; } - struct OFTLimit { uint256 minAmountLD; uint256 maxAmountLD; } - struct OFTFeeDetail { int256 feeAmountLD; string description; } - struct OFTReceipt { uint256 amountSentLD; uint256 amountReceivedLD; } + struct MessagingFee { + uint256 nativeFee; + uint256 lzTokenFee; + } + struct OFTLimit { + uint256 minAmountLD; + uint256 maxAmountLD; + } + struct OFTFeeDetail { + int256 feeAmountLD; + string description; + } + struct OFTReceipt { + uint256 amountSentLD; + uint256 amountReceivedLD; + } interface Oft { function token() external view returns (address); function peers(uint32 eid) external view returns (bytes32); @@ -72,7 +102,6 @@ pub(super) struct Plan { pub operation: UserOperation, pub created_block: u64, pub expires_at: u64, - pub bridge_fee: u64, } impl Plan { diff --git a/src/modules/usdt/types.rs b/src/modules/usdt/types.rs index 08e28da..45245f3 100644 --- a/src/modules/usdt/types.rs +++ b/src/modules/usdt/types.rs @@ -65,7 +65,6 @@ pub struct UsdtTransfer { pub destination: UsdtDestination, pub amount: u64, pub received_amount: u64, - pub bridge_fee: u64, pub fee: Option, pub is_incoming: bool, pub status: UsdtTransferStatus, diff --git a/src/modules/usdt/user_operation.rs b/src/modules/usdt/user_operation.rs index 9dda977..abe1ff8 100644 --- a/src/modules/usdt/user_operation.rs +++ b/src/modules/usdt/user_operation.rs @@ -156,7 +156,7 @@ impl UserOperation { } } -fn sign_hash(hash: B256, key: &SecretKey) -> Result { +pub(super) fn sign_hash(hash: B256, key: &SecretKey) -> Result { let (recovery, signature) = Secp256k1::new() .sign_ecdsa_recoverable(&Message::from_digest(hash.0), key) .serialize_compact(); diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 1636aa2..64ec4ab 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -57,7 +57,7 @@ impl UsdtWallet { store, operation: Mutex::new(()), bridge_poll_offset: AtomicUsize::new(0), - history_range_limit: AtomicU64::new(10_000_000), + history_range_limit: AtomicU64::new(super::history::MAX_LOG_RANGE), })) } @@ -130,7 +130,6 @@ impl UsdtWallet { operation, created_block, expires_at: operation_expires_at, - bridge_fee, }, })?; Ok(quote) @@ -142,12 +141,14 @@ impl UsdtWallet { mnemonic: String, passphrase: Option, ) -> Result { + let mnemonic = zeroize::Zeroizing::new(mnemonic); + let passphrase = passphrase.map(zeroize::Zeroizing::new); let _guard = self.operation.lock().await; - let key = derive_key(mnemonic, passphrase)?; - if super::keys::key_address(&key) != self.address { - return Err(UsdtError::InvalidCredentials); - } if let Some(existing) = self.store.transfer("e_id)? { + let key = derive_key(mnemonic, passphrase)?; + if super::keys::key_address(&key) != self.address { + return Err(UsdtError::InvalidCredentials); + } return Ok(existing); } self.store.require_no_pending()?; @@ -174,10 +175,15 @@ impl UsdtWallet { { return Err(UsdtError::QuoteExpired); } + let key = derive_key(mnemonic, passphrase)?; + if super::keys::key_address(&key) != self.address { + return Err(UsdtError::InvalidCredentials); + } if data.quote.expires_at <= now() + 5 { return Err(UsdtError::QuoteExpired); } let (hash, raw) = data.plan.sign(&key)?; + drop(key); let transfer = UsdtTransfer { id: quote_id, tx_hash: String::new(), @@ -187,7 +193,6 @@ impl UsdtWallet { destination: data.quote.destination, amount: data.quote.amount, received_amount: data.quote.received_amount, - bridge_fee: data.plan.bridge_fee, fee: None, is_incoming: false, status: UsdtTransferStatus::Pending, @@ -203,10 +208,7 @@ impl UsdtWallet { pub async fn sync_history(&self) -> Result { let _guard = self.operation.lock().await; self.rpc.verify_chain().await?; - match tokio::time::timeout(std::time::Duration::from_secs(20), self.scan_history()).await { - Ok(result) => result.map(|()| true), - Err(_) => Ok(false), - } + self.scan_history().await } pub fn history(&self) -> Result, UsdtError> { @@ -264,10 +266,15 @@ impl UsdtWallet { if nonce > plan.operation.nonce { // A nonce advance alone cannot distinguish this payment from a replacement. let block = self.nonce_consumed_block(&plan, confirmed_tip).await?; - let candidates: Vec = self.rpc.call("eth_getLogs", json!([{ + let candidates: Vec = match self.rpc.call("eth_getLogs", json!([{ "address": ENTRY_POINT, "fromBlock": U256::from(block), "toBlock": U256::from(block), "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, null, self.address.into_word()] - }])).await?; + }])).await { + Ok(logs) => logs, + Err(UsdtError::LogRangeTooLarge) => Vec::new(), + Err(error) => return Err(error), + }; + let mut matched = false; for log in candidates .iter() .filter(|log| log["removed"].as_bool() != Some(true)) @@ -292,14 +299,22 @@ impl UsdtWallet { self.settle_from_log(&mut transfer, log, event).await?; } else { transfer.status = UsdtTransferStatus::Replaced; + transfer.received_amount = 0; + transfer.fee = Some(0); self.store.update_transfer(&transfer)?; } + matched = true; break; } + if !matched { + self.reconcile_consumed_nonce(&mut transfer, &plan, block) + .await?; + } } else { let expired = self.block_timestamp(confirmed_tip).await? > plan.expires_at; if nonce == plan.operation.nonce && expired { transfer.status = UsdtTransferStatus::Failed; + transfer.received_amount = 0; transfer.fee = Some(0); self.store.update_transfer(&transfer)?; } else if !expired { @@ -313,6 +328,65 @@ impl UsdtWallet { } impl UsdtWallet { + async fn reconcile_consumed_nonce( + &self, + transfer: &mut UsdtTransfer, + plan: &Plan, + number: u64, + ) -> Result<(), UsdtError> { + let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(20); + let block = self.rpc.block(number).await?; + let block_hash = format!("{:#x}", block.hash); + let start = self.store.nonce_recovery(&transfer.id, &block_hash)?; + if start > block.transactions.len() { + return Err(UsdtError::InvalidResponse); + } + for (index, hash) in block.transactions.iter().enumerate().skip(start) { + if tokio::time::Instant::now() >= deadline { + return Ok(()); + } + let receipt = self.rpc.block_receipt(*hash, &block, number).await?; + for log in receipt["logs"] + .as_array() + .ok_or(UsdtError::InvalidResponse)? + { + if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT { + continue; + } + let Ok(event) = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) + else { + continue; + }; + if event.sender != self.address || event.nonce != plan.operation.nonce { + continue; + } + if event.userOpHash == plan.operation.hash(CHAIN_ID)? { + if event.paymaster != PAYMASTER { + return Err(UsdtError::InvalidResponse); + } + transfer.tx_hash = format!("{hash:#x}"); + transfer.explorer_url = format!("{EXPLORER}/tx/{hash:#x}"); + self.settle(transfer, &receipt, event.success)?; + } else { + transfer.status = UsdtTransferStatus::Replaced; + transfer.received_amount = 0; + transfer.fee = Some(0); + } + return self.store.update_transfer(transfer); + } + self.store + .save_nonce_recovery(&transfer.id, &block_hash, index + 1)?; + } + // Empty indexed logs are insufficient; every receipt in the consuming block must be checked. + if self.rpc.block(number).await?.hash != block.hash { + return Err(UsdtError::NetworkUnavailable); + } + transfer.status = UsdtTransferStatus::Replaced; + transfer.received_amount = 0; + transfer.fee = Some(0); + self.store.update_transfer(transfer) + } + async fn refresh_bridges(&self, transfers: &[UsdtTransfer]) -> Result<(), UsdtError> { let mut bridges: Vec<_> = transfers .iter() @@ -511,9 +585,9 @@ impl UsdtWallet { } } } - transfer.bridge_fee = bridge_fee.unwrap_or(0); - transfer.fee = gas_fee.and_then(|fee| fee.checked_add(transfer.bridge_fee)); + transfer.fee = gas_fee.and_then(|fee| fee.checked_add(bridge_fee.unwrap_or(0))); transfer.status = if !success { + transfer.received_amount = 0; UsdtTransferStatus::Failed } else if transfer.destination == UsdtDestination::Arbitrum { UsdtTransferStatus::Confirmed From d3f11f0cc8c512099ced2e4f20d762885648e9a7 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 24 Sep 2026 18:21:49 +0300 Subject: [PATCH 3/9] test: allow rpc budget in bridge concurrency test --- src/modules/usdt/tests.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index d4bc3f5..b614ef7 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -1662,7 +1662,7 @@ async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending }; use tokio::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, - time::{Duration, Instant}, + time::Duration, }; let chain = MockChain::start().await; let directory = tempfile::tempdir().unwrap(); @@ -1737,12 +1737,11 @@ async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending state.tip += 3; state.timestamp += alloy_primitives::U256::from(601); } - let started = Instant::now(); - let history = tokio::time::timeout(Duration::from_secs(9), wallet.refresh_transfers()) + // Completion includes bridge polling and the shared chain/bundler request budget. + let history = tokio::time::timeout(Duration::from_secs(15), wallet.refresh_transfers()) .await .unwrap() .unwrap(); - assert!(started.elapsed() < Duration::from_secs(9)); assert_eq!(attempts.lock().unwrap().len(), 1); assert_eq!( history @@ -1775,7 +1774,7 @@ async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending .await .unwrap(); let result = tokio::time::timeout( - Duration::from_secs(9), + Duration::from_secs(15), wallet.send(quote.id, TEST_PHRASE.into(), None), ) .await; @@ -1788,7 +1787,7 @@ async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending // Healthy responses slower than an equal share of the budget must still settle. stalled.store(false, Ordering::SeqCst); for _ in 0..2 { - tokio::time::timeout(Duration::from_secs(9), wallet.refresh_transfers()) + tokio::time::timeout(Duration::from_secs(15), wallet.refresh_transfers()) .await .unwrap() .unwrap(); From e01d4121f5add0a4eac1722b749eb38e808c5d79 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 24 Sep 2026 19:05:10 +0300 Subject: [PATCH 4/9] refactor: isolate arbitrum wallet from bridging --- bindings/ios/bitkitcore.swift | 1120 +---------------- bindings/ios/bitkitcoreFFI.h | 78 +- src/lib.rs | 6 +- src/modules/usdt/README.md | 18 +- src/modules/usdt/deposits.rs | 359 ------ src/modules/usdt/errors.rs | 4 - .../usdt/fixtures/deposit-signature.json | 4 - src/modules/usdt/history.rs | 52 +- src/modules/usdt/mod.rs | 2 - src/modules/usdt/paymaster.rs | 7 - src/modules/usdt/rpc.rs | 69 +- src/modules/usdt/store.rs | 19 +- src/modules/usdt/tests.rs | 455 +------ src/modules/usdt/transaction.rs | 41 - src/modules/usdt/types.rs | 29 - src/modules/usdt/wallet.rs | 248 +--- 16 files changed, 135 insertions(+), 2376 deletions(-) delete mode 100644 src/modules/usdt/deposits.rs delete mode 100644 src/modules/usdt/fixtures/deposit-signature.json diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index c07f0bd..4c409a7 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -2563,230 +2563,13 @@ public func FfiConverterTypeUrDecoder_lower(_ value: UrDecoder) -> UnsafeMutable -public protocol UsdtDepositClientProtocol: AnyObject, Sendable { - - func detail(depositId: String, offset: UInt32, mnemonic: String, passphrase: String?) async throws -> UsdtDepositDetail - - func history(offset: UInt32, mnemonic: String, passphrase: String?) async throws -> UsdtDepositPage - - func networks() async throws -> [UsdtDepositNetwork] - - func receive(network: UsdtDepositNetwork, amount: UInt64, mnemonic: String, passphrase: String?) async throws -> UsdtDepositAddress - - func requestRefund(depositId: String, offset: UInt32, refundAddress: String, network: UsdtDepositNetwork, mnemonic: String, passphrase: String?) async throws - -} -open class UsdtDepositClient: UsdtDepositClientProtocol, @unchecked Sendable { - fileprivate let pointer: UnsafeMutableRawPointer! - - /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public struct NoPointer { - public init() {} - } - - // TODO: We'd like this to be `private` but for Swifty reasons, - // we can't implement `FfiConverter` without making this `required` and we can't - // make it `required` without making it `public`. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { - self.pointer = pointer - } - - // This constructor can be used to instantiate a fake object. - // - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. - // - // - Warning: - // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public init(noPointer: NoPointer) { - self.pointer = nil - } - -#if swift(>=5.8) - @_documentation(visibility: private) -#endif - public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_bitkitcore_fn_clone_usdtdepositclient(self.pointer, $0) } - } -public convenience init(address: String, serviceUrl: String)throws { - let pointer = - try rustCallWithError(FfiConverterTypeUsdtError_lift) { - uniffi_bitkitcore_fn_constructor_usdtdepositclient_new( - FfiConverterString.lower(address), - FfiConverterString.lower(serviceUrl),$0 - ) -} - self.init(unsafeFromRawPointer: pointer) -} - - deinit { - guard let pointer = pointer else { - return - } - - try! rustCall { uniffi_bitkitcore_fn_free_usdtdepositclient(pointer, $0) } - } - - - - -open func detail(depositId: String, offset: UInt32, mnemonic: String, passphrase: String?)async throws -> UsdtDepositDetail { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtdepositclient_detail( - self.uniffiClonePointer(), - FfiConverterString.lower(depositId),FfiConverterUInt32.lower(offset),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, - completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, - freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, - liftFunc: FfiConverterTypeUsdtDepositDetail_lift, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - -open func history(offset: UInt32, mnemonic: String, passphrase: String?)async throws -> UsdtDepositPage { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtdepositclient_history( - self.uniffiClonePointer(), - FfiConverterUInt32.lower(offset),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, - completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, - freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, - liftFunc: FfiConverterTypeUsdtDepositPage_lift, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - -open func networks()async throws -> [UsdtDepositNetwork] { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtdepositclient_networks( - self.uniffiClonePointer() - - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, - completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, - freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, - liftFunc: FfiConverterSequenceTypeUsdtDepositNetwork.lift, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - -open func receive(network: UsdtDepositNetwork, amount: UInt64, mnemonic: String, passphrase: String?)async throws -> UsdtDepositAddress { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtdepositclient_receive( - self.uniffiClonePointer(), - FfiConverterTypeUsdtDepositNetwork_lower(network),FfiConverterUInt64.lower(amount),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, - completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, - freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, - liftFunc: FfiConverterTypeUsdtDepositAddress_lift, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - -open func requestRefund(depositId: String, offset: UInt32, refundAddress: String, network: UsdtDepositNetwork, mnemonic: String, passphrase: String?)async throws { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtdepositclient_request_refund( - self.uniffiClonePointer(), - FfiConverterString.lower(depositId),FfiConverterUInt32.lower(offset),FfiConverterString.lower(refundAddress),FfiConverterTypeUsdtDepositNetwork_lower(network),FfiConverterString.lower(mnemonic),FfiConverterOptionString.lower(passphrase) - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_void, - completeFunc: ffi_bitkitcore_rust_future_complete_void, - freeFunc: ffi_bitkitcore_rust_future_free_void, - liftFunc: { $0 }, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - - -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositClient: FfiConverter { - - typealias FfiType = UnsafeMutableRawPointer - typealias SwiftType = UsdtDepositClient - - public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtDepositClient { - return UsdtDepositClient(unsafeFromRawPointer: pointer) - } - - public static func lower(_ value: UsdtDepositClient) -> UnsafeMutableRawPointer { - return value.uniffiClonePointer() - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositClient { - let v: UInt64 = try readInt(&buf) - // The Rust code won't compile if a pointer won't fit in a UInt64. - // We have to go via `UInt` because that's the thing that's the size of a pointer. - let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) - if (ptr == nil) { - throw UniffiInternalError.unexpectedNullPointer - } - return try lift(ptr!) - } - - public static func write(_ value: UsdtDepositClient, into buf: inout [UInt8]) { - // This fiddling is because `Int` is the thing that's the same size as a pointer. - // The Rust code won't compile if a pointer won't fit in a `UInt64`. - writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositClient_lift(_ pointer: UnsafeMutableRawPointer) throws -> UsdtDepositClient { - return try FfiConverterTypeUsdtDepositClient.lift(pointer) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositClient_lower(_ value: UsdtDepositClient) -> UnsafeMutableRawPointer { - return FfiConverterTypeUsdtDepositClient.lower(value) -} - - - - - - public protocol UsdtWalletProtocol: AnyObject, Sendable { func balance() async throws -> UInt64 func history() throws -> [UsdtTransfer] - func quoteTransfer(recipient: String, amount: UInt64, destination: UsdtDestination) async throws -> UsdtQuote + func quoteTransfer(recipient: String, amount: UInt64) async throws -> UsdtQuote func receiveAddress() -> String @@ -2886,13 +2669,13 @@ open func history()throws -> [UsdtTransfer] { }) } -open func quoteTransfer(recipient: String, amount: UInt64, destination: UsdtDestination)async throws -> UsdtQuote { +open func quoteTransfer(recipient: String, amount: UInt64)async throws -> UsdtQuote { return try await uniffiRustCallAsync( rustFutureFunc: { uniffi_bitkitcore_fn_method_usdtwallet_quote_transfer( self.uniffiClonePointer(), - FfiConverterString.lower(recipient),FfiConverterUInt64.lower(amount),FfiConverterTypeUsdtDestination_lower(destination) + FfiConverterString.lower(recipient),FfiConverterUInt64.lower(amount) ) }, pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, @@ -16087,516 +15870,20 @@ extension UrDecoderStatus: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrDecoderStatus: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrDecoderStatus { - return - try UrDecoderStatus( - progress: FfiConverterDouble.read(from: &buf), - fragmentCount: FfiConverterUInt32.read(from: &buf), - payload: FfiConverterOptionTypeUrPayload.read(from: &buf) - ) - } - - public static func write(_ value: UrDecoderStatus, into buf: inout [UInt8]) { - FfiConverterDouble.write(value.progress, into: &buf) - FfiConverterUInt32.write(value.fragmentCount, into: &buf) - FfiConverterOptionTypeUrPayload.write(value.payload, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUrDecoderStatus_lift(_ buf: RustBuffer) throws -> UrDecoderStatus { - return try FfiConverterTypeUrDecoderStatus.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUrDecoderStatus_lower(_ value: UrDecoderStatus) -> RustBuffer { - return FfiConverterTypeUrDecoderStatus.lower(value) -} - - -public struct UsdtDeposit { - public var id: String - public var network: String - public var asset: String - public var amount: UInt64? - public var sourceTx: String - public var status: String - public var code: String? - public var refundTx: String? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(id: String, network: String, asset: String, amount: UInt64?, sourceTx: String, status: String, code: String?, refundTx: String?) { - self.id = id - self.network = network - self.asset = asset - self.amount = amount - self.sourceTx = sourceTx - self.status = status - self.code = code - self.refundTx = refundTx - } -} - -#if compiler(>=6) -extension UsdtDeposit: Sendable {} -#endif - - -extension UsdtDeposit: Equatable, Hashable { - public static func ==(lhs: UsdtDeposit, rhs: UsdtDeposit) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.network != rhs.network { - return false - } - if lhs.asset != rhs.asset { - return false - } - if lhs.amount != rhs.amount { - return false - } - if lhs.sourceTx != rhs.sourceTx { - return false - } - if lhs.status != rhs.status { - return false - } - if lhs.code != rhs.code { - return false - } - if lhs.refundTx != rhs.refundTx { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(id) - hasher.combine(network) - hasher.combine(asset) - hasher.combine(amount) - hasher.combine(sourceTx) - hasher.combine(status) - hasher.combine(code) - hasher.combine(refundTx) - } -} - -extension UsdtDeposit: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDeposit: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDeposit { - return - try UsdtDeposit( - id: FfiConverterString.read(from: &buf), - network: FfiConverterString.read(from: &buf), - asset: FfiConverterString.read(from: &buf), - amount: FfiConverterOptionUInt64.read(from: &buf), - sourceTx: FfiConverterString.read(from: &buf), - status: FfiConverterString.read(from: &buf), - code: FfiConverterOptionString.read(from: &buf), - refundTx: FfiConverterOptionString.read(from: &buf) - ) - } - - public static func write(_ value: UsdtDeposit, into buf: inout [UInt8]) { - FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.network, into: &buf) - FfiConverterString.write(value.asset, into: &buf) - FfiConverterOptionUInt64.write(value.amount, into: &buf) - FfiConverterString.write(value.sourceTx, into: &buf) - FfiConverterString.write(value.status, into: &buf) - FfiConverterOptionString.write(value.code, into: &buf) - FfiConverterOptionString.write(value.refundTx, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDeposit_lift(_ buf: RustBuffer) throws -> UsdtDeposit { - return try FfiConverterTypeUsdtDeposit.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDeposit_lower(_ value: UsdtDeposit) -> RustBuffer { - return FfiConverterTypeUsdtDeposit.lower(value) -} - - -public struct UsdtDepositAddress { - public var network: UsdtDepositNetwork - public var address: String - public var recipient: String - public var amount: UInt64 - public var estimatedReceived: UInt64 - public var minUsdCents: String? - public var maxUsdCents: String? - public var slippageBps: UInt32 - public var uri: String - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(network: UsdtDepositNetwork, address: String, recipient: String, amount: UInt64, estimatedReceived: UInt64, minUsdCents: String?, maxUsdCents: String?, slippageBps: UInt32, uri: String) { - self.network = network - self.address = address - self.recipient = recipient - self.amount = amount - self.estimatedReceived = estimatedReceived - self.minUsdCents = minUsdCents - self.maxUsdCents = maxUsdCents - self.slippageBps = slippageBps - self.uri = uri - } -} - -#if compiler(>=6) -extension UsdtDepositAddress: Sendable {} -#endif - - -extension UsdtDepositAddress: Equatable, Hashable { - public static func ==(lhs: UsdtDepositAddress, rhs: UsdtDepositAddress) -> Bool { - if lhs.network != rhs.network { - return false - } - if lhs.address != rhs.address { - return false - } - if lhs.recipient != rhs.recipient { - return false - } - if lhs.amount != rhs.amount { - return false - } - if lhs.estimatedReceived != rhs.estimatedReceived { - return false - } - if lhs.minUsdCents != rhs.minUsdCents { - return false - } - if lhs.maxUsdCents != rhs.maxUsdCents { - return false - } - if lhs.slippageBps != rhs.slippageBps { - return false - } - if lhs.uri != rhs.uri { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(network) - hasher.combine(address) - hasher.combine(recipient) - hasher.combine(amount) - hasher.combine(estimatedReceived) - hasher.combine(minUsdCents) - hasher.combine(maxUsdCents) - hasher.combine(slippageBps) - hasher.combine(uri) - } -} - -extension UsdtDepositAddress: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositAddress: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositAddress { - return - try UsdtDepositAddress( - network: FfiConverterTypeUsdtDepositNetwork.read(from: &buf), - address: FfiConverterString.read(from: &buf), - recipient: FfiConverterString.read(from: &buf), - amount: FfiConverterUInt64.read(from: &buf), - estimatedReceived: FfiConverterUInt64.read(from: &buf), - minUsdCents: FfiConverterOptionString.read(from: &buf), - maxUsdCents: FfiConverterOptionString.read(from: &buf), - slippageBps: FfiConverterUInt32.read(from: &buf), - uri: FfiConverterString.read(from: &buf) - ) - } - - public static func write(_ value: UsdtDepositAddress, into buf: inout [UInt8]) { - FfiConverterTypeUsdtDepositNetwork.write(value.network, into: &buf) - FfiConverterString.write(value.address, into: &buf) - FfiConverterString.write(value.recipient, into: &buf) - FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterUInt64.write(value.estimatedReceived, into: &buf) - FfiConverterOptionString.write(value.minUsdCents, into: &buf) - FfiConverterOptionString.write(value.maxUsdCents, into: &buf) - FfiConverterUInt32.write(value.slippageBps, into: &buf) - FfiConverterString.write(value.uri, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositAddress_lift(_ buf: RustBuffer) throws -> UsdtDepositAddress { - return try FfiConverterTypeUsdtDepositAddress.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositAddress_lower(_ value: UsdtDepositAddress) -> RustBuffer { - return FfiConverterTypeUsdtDepositAddress.lower(value) -} - - -public struct UsdtDepositDetail { - public var deposit: UsdtDeposit - public var order: UsdtDepositOrder? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(deposit: UsdtDeposit, order: UsdtDepositOrder?) { - self.deposit = deposit - self.order = order - } -} - -#if compiler(>=6) -extension UsdtDepositDetail: Sendable {} -#endif - - -extension UsdtDepositDetail: Equatable, Hashable { - public static func ==(lhs: UsdtDepositDetail, rhs: UsdtDepositDetail) -> Bool { - if lhs.deposit != rhs.deposit { - return false - } - if lhs.order != rhs.order { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(deposit) - hasher.combine(order) - } -} - -extension UsdtDepositDetail: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositDetail: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositDetail { - return - try UsdtDepositDetail( - deposit: FfiConverterTypeUsdtDeposit.read(from: &buf), - order: FfiConverterOptionTypeUsdtDepositOrder.read(from: &buf) - ) - } - - public static func write(_ value: UsdtDepositDetail, into buf: inout [UInt8]) { - FfiConverterTypeUsdtDeposit.write(value.deposit, into: &buf) - FfiConverterOptionTypeUsdtDepositOrder.write(value.order, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositDetail_lift(_ buf: RustBuffer) throws -> UsdtDepositDetail { - return try FfiConverterTypeUsdtDepositDetail.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositDetail_lower(_ value: UsdtDepositDetail) -> RustBuffer { - return FfiConverterTypeUsdtDepositDetail.lower(value) -} - - -public struct UsdtDepositOrder { - public var status: String - public var amountIn: UInt64? - public var amountOut: UInt64? - public var destinationTx: String? - public var refundTx: String? - public var code: String? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(status: String, amountIn: UInt64?, amountOut: UInt64?, destinationTx: String?, refundTx: String?, code: String?) { - self.status = status - self.amountIn = amountIn - self.amountOut = amountOut - self.destinationTx = destinationTx - self.refundTx = refundTx - self.code = code - } -} - -#if compiler(>=6) -extension UsdtDepositOrder: Sendable {} -#endif - - -extension UsdtDepositOrder: Equatable, Hashable { - public static func ==(lhs: UsdtDepositOrder, rhs: UsdtDepositOrder) -> Bool { - if lhs.status != rhs.status { - return false - } - if lhs.amountIn != rhs.amountIn { - return false - } - if lhs.amountOut != rhs.amountOut { - return false - } - if lhs.destinationTx != rhs.destinationTx { - return false - } - if lhs.refundTx != rhs.refundTx { - return false - } - if lhs.code != rhs.code { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(status) - hasher.combine(amountIn) - hasher.combine(amountOut) - hasher.combine(destinationTx) - hasher.combine(refundTx) - hasher.combine(code) - } -} - -extension UsdtDepositOrder: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositOrder: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositOrder { - return - try UsdtDepositOrder( - status: FfiConverterString.read(from: &buf), - amountIn: FfiConverterOptionUInt64.read(from: &buf), - amountOut: FfiConverterOptionUInt64.read(from: &buf), - destinationTx: FfiConverterOptionString.read(from: &buf), - refundTx: FfiConverterOptionString.read(from: &buf), - code: FfiConverterOptionString.read(from: &buf) - ) - } - - public static func write(_ value: UsdtDepositOrder, into buf: inout [UInt8]) { - FfiConverterString.write(value.status, into: &buf) - FfiConverterOptionUInt64.write(value.amountIn, into: &buf) - FfiConverterOptionUInt64.write(value.amountOut, into: &buf) - FfiConverterOptionString.write(value.destinationTx, into: &buf) - FfiConverterOptionString.write(value.refundTx, into: &buf) - FfiConverterOptionString.write(value.code, into: &buf) - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositOrder_lift(_ buf: RustBuffer) throws -> UsdtDepositOrder { - return try FfiConverterTypeUsdtDepositOrder.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositOrder_lower(_ value: UsdtDepositOrder) -> RustBuffer { - return FfiConverterTypeUsdtDepositOrder.lower(value) -} - - -public struct UsdtDepositPage { - public var deposits: [UsdtDeposit] - public var nextOffset: UInt32? - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(deposits: [UsdtDeposit], nextOffset: UInt32?) { - self.deposits = deposits - self.nextOffset = nextOffset - } -} - -#if compiler(>=6) -extension UsdtDepositPage: Sendable {} -#endif - - -extension UsdtDepositPage: Equatable, Hashable { - public static func ==(lhs: UsdtDepositPage, rhs: UsdtDepositPage) -> Bool { - if lhs.deposits != rhs.deposits { - return false - } - if lhs.nextOffset != rhs.nextOffset { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(deposits) - hasher.combine(nextOffset) - } -} - -extension UsdtDepositPage: Codable {} - - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositPage: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositPage { +public struct FfiConverterTypeUrDecoderStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrDecoderStatus { return - try UsdtDepositPage( - deposits: FfiConverterSequenceTypeUsdtDeposit.read(from: &buf), - nextOffset: FfiConverterOptionUInt32.read(from: &buf) + try UrDecoderStatus( + progress: FfiConverterDouble.read(from: &buf), + fragmentCount: FfiConverterUInt32.read(from: &buf), + payload: FfiConverterOptionTypeUrPayload.read(from: &buf) ) } - public static func write(_ value: UsdtDepositPage, into buf: inout [UInt8]) { - FfiConverterSequenceTypeUsdtDeposit.write(value.deposits, into: &buf) - FfiConverterOptionUInt32.write(value.nextOffset, into: &buf) + public static func write(_ value: UrDecoderStatus, into buf: inout [UInt8]) { + FfiConverterDouble.write(value.progress, into: &buf) + FfiConverterUInt32.write(value.fragmentCount, into: &buf) + FfiConverterOptionTypeUrPayload.write(value.payload, into: &buf) } } @@ -16604,15 +15891,15 @@ public struct FfiConverterTypeUsdtDepositPage: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUsdtDepositPage_lift(_ buf: RustBuffer) throws -> UsdtDepositPage { - return try FfiConverterTypeUsdtDepositPage.lift(buf) +public func FfiConverterTypeUrDecoderStatus_lift(_ buf: RustBuffer) throws -> UrDecoderStatus { + return try FfiConverterTypeUrDecoderStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUsdtDepositPage_lower(_ value: UsdtDepositPage) -> RustBuffer { - return FfiConverterTypeUsdtDepositPage.lower(value) +public func FfiConverterTypeUrDecoderStatus_lower(_ value: UrDecoderStatus) -> RustBuffer { + return FfiConverterTypeUrDecoderStatus.lower(value) } @@ -16691,20 +15978,16 @@ public func FfiConverterTypeUsdtPaymentRequest_lower(_ value: UsdtPaymentRequest public struct UsdtQuote { public var id: String public var recipient: String - public var destination: UsdtDestination public var amount: UInt64 - public var receivedAmount: UInt64 public var maximumFee: UInt64 public var expiresAt: UInt64 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, maximumFee: UInt64, expiresAt: UInt64) { + public init(id: String, recipient: String, amount: UInt64, maximumFee: UInt64, expiresAt: UInt64) { self.id = id self.recipient = recipient - self.destination = destination self.amount = amount - self.receivedAmount = receivedAmount self.maximumFee = maximumFee self.expiresAt = expiresAt } @@ -16723,15 +16006,9 @@ extension UsdtQuote: Equatable, Hashable { if lhs.recipient != rhs.recipient { return false } - if lhs.destination != rhs.destination { - return false - } if lhs.amount != rhs.amount { return false } - if lhs.receivedAmount != rhs.receivedAmount { - return false - } if lhs.maximumFee != rhs.maximumFee { return false } @@ -16744,9 +16021,7 @@ extension UsdtQuote: Equatable, Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(recipient) - hasher.combine(destination) hasher.combine(amount) - hasher.combine(receivedAmount) hasher.combine(maximumFee) hasher.combine(expiresAt) } @@ -16765,9 +16040,7 @@ public struct FfiConverterTypeUsdtQuote: FfiConverterRustBuffer { try UsdtQuote( id: FfiConverterString.read(from: &buf), recipient: FfiConverterString.read(from: &buf), - destination: FfiConverterTypeUsdtDestination.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf), - receivedAmount: FfiConverterUInt64.read(from: &buf), maximumFee: FfiConverterUInt64.read(from: &buf), expiresAt: FfiConverterUInt64.read(from: &buf) ) @@ -16776,9 +16049,7 @@ public struct FfiConverterTypeUsdtQuote: FfiConverterRustBuffer { public static func write(_ value: UsdtQuote, into buf: inout [UInt8]) { FfiConverterString.write(value.id, into: &buf) FfiConverterString.write(value.recipient, into: &buf) - FfiConverterTypeUsdtDestination.write(value.destination, into: &buf) FfiConverterUInt64.write(value.amount, into: &buf) - FfiConverterUInt64.write(value.receivedAmount, into: &buf) FfiConverterUInt64.write(value.maximumFee, into: &buf) FfiConverterUInt64.write(value.expiresAt, into: &buf) } @@ -16804,9 +16075,7 @@ public struct UsdtTransfer { public var id: String public var txHash: String public var userOperationHash: String? - public var bridgeGuid: String? public var recipient: String - public var destination: UsdtDestination public var amount: UInt64 public var receivedAmount: UInt64 public var fee: UInt64? @@ -16817,13 +16086,11 @@ public struct UsdtTransfer { // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, txHash: String, userOperationHash: String?, bridgeGuid: String?, recipient: String, destination: UsdtDestination, amount: UInt64, receivedAmount: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { + public init(id: String, txHash: String, userOperationHash: String?, recipient: String, amount: UInt64, receivedAmount: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { self.id = id self.txHash = txHash self.userOperationHash = userOperationHash - self.bridgeGuid = bridgeGuid self.recipient = recipient - self.destination = destination self.amount = amount self.receivedAmount = receivedAmount self.fee = fee @@ -16850,15 +16117,9 @@ extension UsdtTransfer: Equatable, Hashable { if lhs.userOperationHash != rhs.userOperationHash { return false } - if lhs.bridgeGuid != rhs.bridgeGuid { - return false - } if lhs.recipient != rhs.recipient { return false } - if lhs.destination != rhs.destination { - return false - } if lhs.amount != rhs.amount { return false } @@ -16887,9 +16148,7 @@ extension UsdtTransfer: Equatable, Hashable { hasher.combine(id) hasher.combine(txHash) hasher.combine(userOperationHash) - hasher.combine(bridgeGuid) hasher.combine(recipient) - hasher.combine(destination) hasher.combine(amount) hasher.combine(receivedAmount) hasher.combine(fee) @@ -16914,9 +16173,7 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { id: FfiConverterString.read(from: &buf), txHash: FfiConverterString.read(from: &buf), userOperationHash: FfiConverterOptionString.read(from: &buf), - bridgeGuid: FfiConverterOptionString.read(from: &buf), recipient: FfiConverterString.read(from: &buf), - destination: FfiConverterTypeUsdtDestination.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf), receivedAmount: FfiConverterUInt64.read(from: &buf), fee: FfiConverterOptionUInt64.read(from: &buf), @@ -16931,9 +16188,7 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { FfiConverterString.write(value.id, into: &buf) FfiConverterString.write(value.txHash, into: &buf) FfiConverterOptionString.write(value.userOperationHash, into: &buf) - FfiConverterOptionString.write(value.bridgeGuid, into: &buf) FfiConverterString.write(value.recipient, into: &buf) - FfiConverterTypeUsdtDestination.write(value.destination, into: &buf) FfiConverterUInt64.write(value.amount, into: &buf) FfiConverterUInt64.write(value.receivedAmount, into: &buf) FfiConverterOptionUInt64.write(value.fee, into: &buf) @@ -24084,171 +23339,6 @@ extension UrPayload: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum UsdtDepositNetwork { - - case ethereum - case tron -} - - -#if compiler(>=6) -extension UsdtDepositNetwork: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDepositNetwork: FfiConverterRustBuffer { - typealias SwiftType = UsdtDepositNetwork - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDepositNetwork { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .ethereum - - case 2: return .tron - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: UsdtDepositNetwork, into buf: inout [UInt8]) { - switch value { - - - case .ethereum: - writeInt(&buf, Int32(1)) - - - case .tron: - writeInt(&buf, Int32(2)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositNetwork_lift(_ buf: RustBuffer) throws -> UsdtDepositNetwork { - return try FfiConverterTypeUsdtDepositNetwork.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDepositNetwork_lower(_ value: UsdtDepositNetwork) -> RustBuffer { - return FfiConverterTypeUsdtDepositNetwork.lower(value) -} - - -extension UsdtDepositNetwork: Equatable, Hashable {} - -extension UsdtDepositNetwork: Codable {} - - - - - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. - -public enum UsdtDestination { - - case stable - case ethereum - case arbitrum - case polygon - case plasma -} - - -#if compiler(>=6) -extension UsdtDestination: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeUsdtDestination: FfiConverterRustBuffer { - typealias SwiftType = UsdtDestination - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UsdtDestination { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .stable - - case 2: return .ethereum - - case 3: return .arbitrum - - case 4: return .polygon - - case 5: return .plasma - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: UsdtDestination, into buf: inout [UInt8]) { - switch value { - - - case .stable: - writeInt(&buf, Int32(1)) - - - case .ethereum: - writeInt(&buf, Int32(2)) - - - case .arbitrum: - writeInt(&buf, Int32(3)) - - - case .polygon: - writeInt(&buf, Int32(4)) - - - case .plasma: - writeInt(&buf, Int32(5)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDestination_lift(_ buf: RustBuffer) throws -> UsdtDestination { - return try FfiConverterTypeUsdtDestination.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeUsdtDestination_lower(_ value: UsdtDestination) -> RustBuffer { - return FfiConverterTypeUsdtDestination.lower(value) -} - - -extension UsdtDestination: Equatable, Hashable {} - -extension UsdtDestination: Codable {} - - - - - - public enum UsdtError: Swift.Error { @@ -24258,13 +23348,11 @@ public enum UsdtError: Swift.Error { case InvalidAddress case WrongNetwork case InvalidCredentials - case ClockSkew case UnsupportedDelegation case InsufficientBalance case QuoteExpired case PendingTransfer case UnsupportedRoute - case DepositNeedsAttention case NotConfigured case NetworkUnavailable case RateLimited @@ -24294,24 +23382,22 @@ public struct FfiConverterTypeUsdtError: FfiConverterRustBuffer { case 2: return .InvalidAddress case 3: return .WrongNetwork case 4: return .InvalidCredentials - case 5: return .ClockSkew - case 6: return .UnsupportedDelegation - case 7: return .InsufficientBalance - case 8: return .QuoteExpired - case 9: return .PendingTransfer - case 10: return .UnsupportedRoute - case 11: return .DepositNeedsAttention - case 12: return .NotConfigured - case 13: return .NetworkUnavailable - case 14: return .RateLimited - case 15: return .LogRangeTooLarge - case 16: return .TransactionRejected( + case 5: return .UnsupportedDelegation + case 6: return .InsufficientBalance + case 7: return .QuoteExpired + case 8: return .PendingTransfer + case 9: return .UnsupportedRoute + case 10: return .NotConfigured + case 11: return .NetworkUnavailable + case 12: return .RateLimited + case 13: return .LogRangeTooLarge + case 14: return .TransactionRejected( reason: try FfiConverterString.read(from: &buf) ) - case 17: return .Storage( + case 15: return .Storage( reason: try FfiConverterString.read(from: &buf) ) - case 18: return .InvalidResponse + case 16: return .InvalidResponse default: throw UniffiInternalError.unexpectedEnumCase } @@ -24340,62 +23426,54 @@ public struct FfiConverterTypeUsdtError: FfiConverterRustBuffer { writeInt(&buf, Int32(4)) - case .ClockSkew: - writeInt(&buf, Int32(5)) - - case .UnsupportedDelegation: - writeInt(&buf, Int32(6)) + writeInt(&buf, Int32(5)) case .InsufficientBalance: - writeInt(&buf, Int32(7)) + writeInt(&buf, Int32(6)) case .QuoteExpired: - writeInt(&buf, Int32(8)) + writeInt(&buf, Int32(7)) case .PendingTransfer: - writeInt(&buf, Int32(9)) + writeInt(&buf, Int32(8)) case .UnsupportedRoute: - writeInt(&buf, Int32(10)) - - - case .DepositNeedsAttention: - writeInt(&buf, Int32(11)) + writeInt(&buf, Int32(9)) case .NotConfigured: - writeInt(&buf, Int32(12)) + writeInt(&buf, Int32(10)) case .NetworkUnavailable: - writeInt(&buf, Int32(13)) + writeInt(&buf, Int32(11)) case .RateLimited: - writeInt(&buf, Int32(14)) + writeInt(&buf, Int32(12)) case .LogRangeTooLarge: - writeInt(&buf, Int32(15)) + writeInt(&buf, Int32(13)) case let .TransactionRejected(reason): - writeInt(&buf, Int32(16)) + writeInt(&buf, Int32(14)) FfiConverterString.write(reason, into: &buf) case let .Storage(reason): - writeInt(&buf, Int32(17)) + writeInt(&buf, Int32(15)) FfiConverterString.write(reason, into: &buf) case .InvalidResponse: - writeInt(&buf, Int32(18)) + writeInt(&buf, Int32(16)) } } @@ -24441,8 +23519,6 @@ public enum UsdtTransferStatus { case pending case confirmed case failed - case bridging - case bridgeNeedsAttention case replaced } @@ -24467,11 +23543,7 @@ public struct FfiConverterTypeUsdtTransferStatus: FfiConverterRustBuffer { case 3: return .failed - case 4: return .bridging - - case 5: return .bridgeNeedsAttention - - case 6: return .replaced + case 4: return .replaced default: throw UniffiInternalError.unexpectedEnumCase } @@ -24493,16 +23565,8 @@ public struct FfiConverterTypeUsdtTransferStatus: FfiConverterRustBuffer { writeInt(&buf, Int32(3)) - case .bridging: - writeInt(&buf, Int32(4)) - - - case .bridgeNeedsAttention: - writeInt(&buf, Int32(5)) - - case .replaced: - writeInt(&buf, Int32(6)) + writeInt(&buf, Int32(4)) } } @@ -25657,30 +24721,6 @@ fileprivate struct FfiConverterOptionTypeTrezorFeatures: FfiConverterRustBuffer } } -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -fileprivate struct FfiConverterOptionTypeUsdtDepositOrder: FfiConverterRustBuffer { - typealias SwiftType = UsdtDepositOrder? - - public static func write(_ value: SwiftType, into buf: inout [UInt8]) { - guard let value = value else { - writeInt(&buf, Int8(0)) - return - } - writeInt(&buf, Int8(1)) - FfiConverterTypeUsdtDepositOrder.write(value, into: &buf) - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { - switch try readInt(&buf) as Int8 { - case 0: return nil - case 1: return try FfiConverterTypeUsdtDepositOrder.read(from: &buf) - default: throw UniffiInternalError.unexpectedOptionalTag - } - } -} - #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -27036,31 +26076,6 @@ fileprivate struct FfiConverterSequenceTypeTxOutput: FfiConverterRustBuffer { } } -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -fileprivate struct FfiConverterSequenceTypeUsdtDeposit: FfiConverterRustBuffer { - typealias SwiftType = [UsdtDeposit] - - public static func write(_ value: [UsdtDeposit], into buf: inout [UInt8]) { - let len = Int32(value.count) - writeInt(&buf, len) - for item in value { - FfiConverterTypeUsdtDeposit.write(item, into: &buf) - } - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UsdtDeposit] { - let len: Int32 = try readInt(&buf) - var seq = [UsdtDeposit]() - seq.reserveCapacity(Int(len)) - for _ in 0 ..< len { - seq.append(try FfiConverterTypeUsdtDeposit.read(from: &buf)) - } - return seq - } -} - #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -27211,31 +26226,6 @@ fileprivate struct FfiConverterSequenceTypeHardwareWalletTransport: FfiConverter } } -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -fileprivate struct FfiConverterSequenceTypeUsdtDepositNetwork: FfiConverterRustBuffer { - typealias SwiftType = [UsdtDepositNetwork] - - public static func write(_ value: [UsdtDepositNetwork], into buf: inout [UInt8]) { - let len = Int32(value.count) - writeInt(&buf, len) - for item in value { - FfiConverterTypeUsdtDepositNetwork.write(item, into: &buf) - } - } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [UsdtDepositNetwork] { - let len: Int32 = try readInt(&buf) - var seq = [UsdtDepositNetwork]() - seq.reserveCapacity(Int(len)) - for _ in 0 ..< len { - seq.append(try FfiConverterTypeUsdtDepositNetwork.read(from: &buf)) - } - return seq - } -} - #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -30667,28 +29657,13 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_method_urdecoder_reset() != 6027) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_method_usdtdepositclient_detail() != 63177) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_bitkitcore_checksum_method_usdtdepositclient_history() != 10976) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_bitkitcore_checksum_method_usdtdepositclient_networks() != 27890) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_bitkitcore_checksum_method_usdtdepositclient_receive() != 27375) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_bitkitcore_checksum_method_usdtdepositclient_request_refund() != 10045) { - return InitializationResult.apiChecksumMismatch - } if (uniffi_bitkitcore_checksum_method_usdtwallet_balance() != 12328) { return InitializationResult.apiChecksumMismatch } if (uniffi_bitkitcore_checksum_method_usdtwallet_history() != 4617) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_method_usdtwallet_quote_transfer() != 3732) { + if (uniffi_bitkitcore_checksum_method_usdtwallet_quote_transfer() != 56645) { return InitializationResult.apiChecksumMismatch } if (uniffi_bitkitcore_checksum_method_usdtwallet_receive_address() != 540) { @@ -30709,9 +29684,6 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_constructor_urdecoder_new() != 23014) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_constructor_usdtdepositclient_new() != 44626) { - return InitializationResult.apiChecksumMismatch - } if (uniffi_bitkitcore_checksum_constructor_usdtwallet_new() != 63633) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index 24fb403..da7fb99 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -660,46 +660,6 @@ RustBuffer uniffi_bitkitcore_fn_method_urdecoder_receive(void*_Nonnull ptr, Rust void uniffi_bitkitcore_fn_method_urdecoder_reset(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status ); #endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTDEPOSITCLIENT -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTDEPOSITCLIENT -void*_Nonnull uniffi_bitkitcore_fn_clone_usdtdepositclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTDEPOSITCLIENT -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FREE_USDTDEPOSITCLIENT -void uniffi_bitkitcore_fn_free_usdtdepositclient(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW -void*_Nonnull uniffi_bitkitcore_fn_constructor_usdtdepositclient_new(RustBuffer address, RustBuffer service_url, RustCallStatus *_Nonnull out_status -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_DETAIL -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_DETAIL -uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_detail(void*_Nonnull ptr, RustBuffer deposit_id, uint32_t offset, RustBuffer mnemonic, RustBuffer passphrase -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_HISTORY -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_HISTORY -uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_history(void*_Nonnull ptr, uint32_t offset, RustBuffer mnemonic, RustBuffer passphrase -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_NETWORKS -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_NETWORKS -uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_networks(void*_Nonnull ptr -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_RECEIVE -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_RECEIVE -uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_receive(void*_Nonnull ptr, RustBuffer network, uint64_t amount, RustBuffer mnemonic, RustBuffer passphrase -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND -uint64_t uniffi_bitkitcore_fn_method_usdtdepositclient_request_refund(void*_Nonnull ptr, RustBuffer deposit_id, uint32_t offset, RustBuffer refund_address, RustBuffer network, RustBuffer mnemonic, RustBuffer passphrase -); -#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTWALLET #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_CLONE_USDTWALLET void*_Nonnull uniffi_bitkitcore_fn_clone_usdtwallet(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status @@ -727,7 +687,7 @@ RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_history(void*_Nonnull ptr, Rus #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_QUOTE_TRANSFER #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_QUOTE_TRANSFER -uint64_t uniffi_bitkitcore_fn_method_usdtwallet_quote_transfer(void*_Nonnull ptr, RustBuffer recipient, uint64_t amount, RustBuffer destination +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_quote_transfer(void*_Nonnull ptr, RustBuffer recipient, uint64_t amount ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_RECEIVE_ADDRESS @@ -3501,36 +3461,6 @@ uint16_t uniffi_bitkitcore_checksum_method_urdecoder_receive(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_URDECODER_RESET uint16_t uniffi_bitkitcore_checksum_method_urdecoder_reset(void -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_DETAIL -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_DETAIL -uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_detail(void - -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_HISTORY -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_HISTORY -uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_history(void - -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_NETWORKS -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_NETWORKS -uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_networks(void - -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_RECEIVE -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_RECEIVE -uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_receive(void - -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTDEPOSITCLIENT_REQUEST_REFUND -uint16_t uniffi_bitkitcore_checksum_method_usdtdepositclient_request_refund(void - ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_BALANCE @@ -3585,12 +3515,6 @@ uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_sync_history(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_URDECODER_NEW uint16_t uniffi_bitkitcore_checksum_constructor_urdecoder_new(void -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTDEPOSITCLIENT_NEW -uint16_t uniffi_bitkitcore_checksum_constructor_usdtdepositclient_new(void - ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_CONSTRUCTOR_USDTWALLET_NEW diff --git a/src/lib.rs b/src/lib.rs index 9266c44..035fb1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,10 +91,8 @@ pub use modules::onchain; pub use modules::scanner::{DecodingError, LnurlPayData, Scanner}; pub use modules::seedqr::{decode_compact_seed_qr, decode_standard_seed_qr, SeedQrError}; pub use modules::usdt::{ - usdt_address, usdt_format_amount, usdt_parse_amount, usdt_parse_payment_request, UsdtDeposit, - UsdtDepositAddress, UsdtDepositClient, UsdtDepositDetail, UsdtDepositNetwork, UsdtDepositOrder, - UsdtDepositPage, UsdtDestination, UsdtError, UsdtPaymentRequest, UsdtQuote, UsdtTransfer, - UsdtTransferStatus, UsdtWallet, + usdt_address, usdt_format_amount, usdt_parse_amount, usdt_parse_payment_request, UsdtError, + UsdtPaymentRequest, UsdtQuote, UsdtTransfer, UsdtTransferStatus, UsdtWallet, }; use bip39::Mnemonic; diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index 9e9e5b1..3f6f9ab 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -14,7 +14,7 @@ Owned mnemonic/passphrase/seed buffers are zeroized and signing keys are erased ## Quotes and fees -`quote_transfer` takes a raw recipient, positive atomic amount and destination; it never receives signing credentials. Local quotes last at most 120 seconds and newly quoted paymaster terms must expire within 15 minutes. `send` validates the owner, nonces, balance, gas estimates, current gas prices and deadlines before signing the stored plan. Changed terms require a new review; signing cannot raise the approved fee. +`quote_transfer` takes a raw recipient, positive atomic amount; it never receives signing credentials. Local quotes last at most 120 seconds and newly quoted paymaster terms must expire within 15 minutes. `send` validates the owner, nonces, balance, gas estimates, current gas prices and deadlines before signing the stored plan. Changed terms require a new review; signing cannot raise the approved fee. The pinned ERC-20 paymaster collects USDT. Its finite approval includes a 5% margin; the displayed maximum fee comes from signed gas limits and paymaster terms, not the allowance. Call/pre-verification estimates receive 10% execution/L1-data headroom; the charged pre-verification margin is included in the maximum. A residual paymaster allowance can remain and is reset to a finite amount on the next payment. @@ -36,23 +36,17 @@ Payment outcomes and expiry decisions trust the configured chain RPC. A maliciou Storage is wallet-specific and owned by the `UsdtWallet` object. Drop it before deleting its database during an explicit wallet wipe. Async exports use UniFFI's Tokio adapter, preserving cancellation of the polled future; they do not detach sends onto the global runtime used by stateless exports. -## Transport and cross-network APIs +## Transport Both chain and bundler endpoints must be controlled, credential-free HTTPS URLs; HTTP is accepted only on loopback for fixtures. Provider keys belong on the server. Chain/bundler calls share an 80/minute budget with a burst of 20. Responses are bounded to 2 MiB, except protocol-projected receipts up to 16 MiB. The companion service documents provider requirements, receipt projection and deployment limits. -`UsdtDepositClient` signs Orchestra deposit registration, history, detail and explicit refund requests for the derived account. It uses a separate optional service endpoint; estimates do not imply delivery. A clock-skew error requires correcting the device clock. Source-network fees are paid by the sender. Partner provisioning, delivered deposits and refund acceptance are separate release checks. - -The outbound bridge API supports Ethereum (30101), Polygon (30109), Plasma (30383) and Stable (30396), alongside direct Arbitrum transfers. Native release flows expose Arbitrum only; bridge routes require explicit service enablement and destination acceptance. Plain deposits on another chain are not automatically forwarded. - -Bridges use the pinned OFT and TransactionValueHelper with zero account ETH, a finite USDT approval covering principal/fee, and atomic helper-allowance revocation. The deployed helper requires native liquidity and retains behaviors noted in its OpenZeppelin audit; its verified runtime is not the audit-remediated implementation. Source success means bridging, not delivered. LayerZero status must match the operation GUID/pathway before confirmation; blocked delivery remains visible and never triggers an automatic paid retry. RPC providers see queried addresses; LayerZero Scan sees bridge transaction hashes. - ## Validation and bindings Run `cargo test --locked --lib modules::usdt`; CI runs these deterministic tests. They cover independent signing/address vectors, fee bounds, uncertain submission, nonce recovery and restored history. Fixtures use public test credentials. -For the ignored deployed-contract test, start a fresh Arbitrum Anvil fork on port 18545 and `tests/usdt-fork/provider.mjs` on 18546 after installing its pinned dependencies. Run `cargo test deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically -- --ignored`. The fixture requires Anvil, sets local balances/signing terms and checks deployed bytecode; it does not establish real provider pricing or destination delivery. +For the ignored deployed-contract test, start a fresh Arbitrum Anvil fork on port 18545 and `tests/usdt-fork/provider.mjs` on 18546 after installing its pinned dependencies. Run `cargo test deployed_contracts_collect_usdt_fees_without_account_eth -- --ignored`. The fixture requires Anvil, sets local balances/signing terms and checks deployed bytecode; it does not establish real provider pricing. -To include the service, start it with `USDT_BRIDGE_NETWORKS=ethereum,polygon,plasma,stable NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then pass `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` to the ignored test. +To include the service, start it with `NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then pass `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` to the ignored test. Build iOS and Android sequentially with the repository scripts; Android temporarily edits the manifest/example. Generated bindings and native artifacts must use the same source. App configuration and local package overrides belong in each native repository's USDT documentation. @@ -64,7 +58,3 @@ Build iOS and Android sequentially with the repository scripts; Android temporar - [Pimlico supported tokens](https://docs.pimlico.io/references/paymaster/erc20-paymaster/supported-tokens) - [Pimlico pricing](https://www.pimlico.io/pricing) - [Pimlico public endpoint limits](https://docs.pimlico.io/references/bundler/public-endpoint) -- [USDT0 documentation](https://docs.usdt0.to/) -- [Transaction helper audit](https://www.openzeppelin.com/news/usdt0-transaction-helper-audit) -- [Verified deployed helper](https://arbitrum.blockscout.com/api/v2/smart-contracts/0xa90f03c856d01f698e7071b393387cd75a8a319a) -- [LayerZero message statuses](https://docs.layerzero.network/v2/tools/layerzeroscan/mainnet/messages/get-messagesstatus) diff --git a/src/modules/usdt/deposits.rs b/src/modules/usdt/deposits.rs deleted file mode 100644 index 12c4114..0000000 --- a/src/modules/usdt/deposits.rs +++ /dev/null @@ -1,359 +0,0 @@ -use super::{ - keys::{derive_key, key_address, parse_address}, - rpc::{bounded_json, endpoint_client}, - user_operation::sign_hash, - UsdtError, -}; -use alloy_primitives::{eip191_hash_message, Address}; -use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; -use serde_json::{json, Value}; -use std::{sync::Arc, time::Duration}; -use zeroize::Zeroizing; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] -#[serde(rename_all = "lowercase")] -pub enum UsdtDepositNetwork { - Ethereum, - Tron, -} - -#[derive(Clone, Debug, Deserialize, uniffi::Record)] -pub struct UsdtDepositAddress { - pub network: UsdtDepositNetwork, - pub address: String, - pub recipient: String, - #[serde(deserialize_with = "number")] - pub amount: u64, - #[serde(deserialize_with = "number")] - pub estimated_received: u64, - pub min_usd_cents: Option, - pub max_usd_cents: Option, - pub slippage_bps: u32, - #[serde(default)] - pub uri: String, -} - -#[derive(Clone, Debug, Deserialize, uniffi::Record)] -pub struct UsdtDeposit { - pub id: String, - pub network: String, - pub asset: String, - #[serde(deserialize_with = "optional_number")] - pub amount: Option, - pub source_tx: String, - pub status: String, - pub code: Option, - pub refund_tx: Option, -} - -#[derive(Clone, Debug, Deserialize, uniffi::Record)] -pub struct UsdtDepositPage { - pub deposits: Vec, - pub next_offset: Option, -} - -#[derive(Clone, Debug, Deserialize, uniffi::Record)] -pub struct UsdtDepositOrder { - pub status: String, - #[serde(deserialize_with = "optional_number")] - pub amount_in: Option, - #[serde(deserialize_with = "optional_number")] - pub amount_out: Option, - pub destination_tx: Option, - pub refund_tx: Option, - pub code: Option, -} - -#[derive(Clone, Debug, Deserialize, uniffi::Record)] -pub struct UsdtDepositDetail { - pub deposit: UsdtDeposit, - pub order: Option, -} - -#[derive(uniffi::Object)] -pub struct UsdtDepositClient { - address: Address, - client: reqwest::Client, - url: String, -} - -#[uniffi::export(async_runtime = "tokio")] -impl UsdtDepositClient { - #[uniffi::constructor] - pub fn new(address: String, service_url: String) -> Result, UsdtError> { - let client = endpoint_client(&service_url, Duration::from_secs(22))?; - Ok(Arc::new(Self { - address: parse_address(&address)?, - client, - url: service_url, - })) - } - - pub async fn networks(&self) -> Result, UsdtError> { - #[derive(Deserialize)] - struct Networks { - networks: Vec, - } - Ok(self - .response::(self.client.get(&self.url)) - .await? - .networks) - } - - pub async fn receive( - &self, - network: UsdtDepositNetwork, - amount: u64, - mnemonic: String, - passphrase: Option, - ) -> Result { - let mnemonic = Zeroizing::new(mnemonic); - let passphrase = passphrase.map(Zeroizing::new); - if amount == 0 { - return Err(UsdtError::InvalidAmount); - } - let mut result: UsdtDepositAddress = self - .call( - json!({"action":"receive", "network":network, - "amount":amount.to_string()}), - mnemonic, - passphrase, - ) - .await?; - if result.network != network - || parse_address(&result.recipient)? != self.address - || result.amount != amount - || result.estimated_received == 0 - || result.slippage_bps != 50 - { - return Err(UsdtError::InvalidResponse); - } - validate_source_address(&result.address, network)?; - result.uri = match network { - UsdtDepositNetwork::Ethereum => format!( - "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7@1/transfer?address={}", - result.address - ), - UsdtDepositNetwork::Tron => result.address.clone(), - }; - Ok(result) - } - - pub async fn history( - &self, - offset: u32, - mnemonic: String, - passphrase: Option, - ) -> Result { - self.call( - json!({"action":"history","offset":offset}), - mnemonic.into(), - passphrase.map(Into::into), - ) - .await - } - - pub async fn detail( - &self, - deposit_id: String, - offset: u32, - mnemonic: String, - passphrase: Option, - ) -> Result { - let result: UsdtDepositDetail = self - .call( - json!({"action":"detail","depositId":deposit_id,"offset":offset}), - mnemonic.into(), - passphrase.map(Into::into), - ) - .await?; - if result.deposit.id != deposit_id { - return Err(UsdtError::InvalidResponse); - } - Ok(result) - } - - pub async fn request_refund( - &self, - deposit_id: String, - offset: u32, - refund_address: String, - network: UsdtDepositNetwork, - mnemonic: String, - passphrase: Option, - ) -> Result<(), UsdtError> { - let mnemonic = Zeroizing::new(mnemonic); - let passphrase = passphrase.map(Zeroizing::new); - validate_source_address(refund_address.trim(), network)?; - let result: Value = self - .call( - json!({"action":"refund","depositId":deposit_id,"offset":offset, - "refundAddress":refund_address.trim()}), - mnemonic, - passphrase, - ) - .await?; - if result["status"] != "refund_requested" { - return Err(UsdtError::InvalidResponse); - } - Ok(()) - } -} - -impl UsdtDepositClient { - async fn call( - &self, - payload: Value, - mnemonic: Zeroizing, - passphrase: Option>, - ) -> Result { - let signed = self.authorize(payload, mnemonic, passphrase, super::wallet::now())?; - self.response(self.client.post(&self.url).json(&signed)) - .await - } - - fn authorize( - &self, - payload: Value, - mnemonic: Zeroizing, - passphrase: Option>, - timestamp: u64, - ) -> Result { - let key = derive_key(mnemonic, passphrase)?; - if key_address(&key) != self.address { - return Err(UsdtError::InvalidCredentials); - } - let request = - json!({"owner":self.address.to_checksum(None),"timestamp":timestamp,"payload":payload}) - .to_string(); - let message = format!("Bitkit USDT deposits v1\n{request}"); - let signature = sign_hash(eip191_hash_message(message), &key)?; - drop(key); - Ok(json!({"request":request,"signature":signature})) - } - - async fn response( - &self, - request: reqwest::RequestBuilder, - ) -> Result { - let response = request - .send() - .await - .map_err(|_| UsdtError::NetworkUnavailable)?; - let status = response.status(); - if status == reqwest::StatusCode::TOO_MANY_REQUESTS { - return Err(UsdtError::RateLimited); - } - let value = bounded_json(response, 262144, UsdtError::InvalidResponse).await; - if !status.is_success() { - let error = value.unwrap_or_default(); - return Err(match error["error"].as_str() { - Some("not_configured") => UsdtError::NotConfigured, - Some("invalid_authorization") => UsdtError::InvalidCredentials, - Some("clock_skew") => UsdtError::ClockSkew, - Some("amount_too_small" | "amount_too_large" | "amount_exceeds_liquidity") => { - UsdtError::InvalidAmount - } - Some("invalid_refund_address") => UsdtError::InvalidAddress, - Some("route_unavailable") => UsdtError::UnsupportedRoute, - Some( - "refund_not_available" - | "instruction_conflict" - | "not_found" - | "operator_required" - | "standing_tron_refund_requires_operator", - ) => UsdtError::DepositNeedsAttention, - _ => UsdtError::NetworkUnavailable, - }); - } - serde_json::from_value(value?).map_err(Into::into) - } -} - -fn validate_source_address(value: &str, network: UsdtDepositNetwork) -> Result<(), UsdtError> { - match network { - UsdtDepositNetwork::Ethereum => { - parse_address(value)?; - } - UsdtDepositNetwork::Tron => { - let payload = - bitcoin::base58::decode_check(value).map_err(|_| UsdtError::InvalidAddress)?; - if payload.len() != 21 || payload[0] != 0x41 { - return Err(UsdtError::InvalidAddress); - } - } - } - Ok(()) -} - -fn number<'de, D: Deserializer<'de>>(deserializer: D) -> Result { - String::deserialize(deserializer)? - .parse() - .map_err(serde::de::Error::custom) -} -fn optional_number<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { - Option::::deserialize(deserializer)? - .map(|v| v.parse().map_err(serde::de::Error::custom)) - .transpose() -} - -#[cfg(test)] -mod tests { - use super::*; - const PHRASE: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - - #[test] - fn deposit_authorization_matches_ethers_and_rejects_another_wallet() { - let vector: Value = - serde_json::from_str(include_str!("fixtures/deposit-signature.json")).unwrap(); - let request: Value = serde_json::from_str(vector["request"].as_str().unwrap()).unwrap(); - let client = UsdtDepositClient::new( - request["owner"].as_str().unwrap().into(), - "https://example.com/v1/usdt/deposits".into(), - ) - .unwrap(); - assert_eq!( - client - .authorize( - request["payload"].clone(), - PHRASE.to_owned().into(), - None, - 1_800_000_000 - ) - .unwrap(), - vector - ); - assert!(matches!( - client.authorize( - request["payload"].clone(), - PHRASE.to_owned().into(), - Some("other".to_owned().into()), - 1_800_000_000 - ), - Err(UsdtError::InvalidCredentials) - )); - } - - #[test] - fn deposit_addresses_and_transport_reject_wrong_networks() { - let tron = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; - assert!(validate_source_address(tron, UsdtDepositNetwork::Tron).is_ok()); - assert!(validate_source_address(tron, UsdtDepositNetwork::Ethereum).is_err()); - assert!(validate_source_address( - "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6s", - UsdtDepositNetwork::Tron - ) - .is_err()); - for url in [ - "http://example.com", - "https://user:password@example.com", - "https://example.com?key=secret", - ] { - assert!(UsdtDepositClient::new( - "0x1111111111111111111111111111111111111111".into(), - url.into() - ) - .is_err()); - } - } -} diff --git a/src/modules/usdt/errors.rs b/src/modules/usdt/errors.rs index 8b15cc2..059f2c2 100644 --- a/src/modules/usdt/errors.rs +++ b/src/modules/usdt/errors.rs @@ -10,8 +10,6 @@ pub enum UsdtError { WrongNetwork, #[error("Wallet credentials do not match this USDT account")] InvalidCredentials, - #[error("Set your device date and time automatically, then try again")] - ClockSkew, #[error("This account uses another wallet's smart account. Restore its delegation before sending with Bitkit")] UnsupportedDelegation, #[error("The USDT balance does not cover the amount and maximum fee")] @@ -22,8 +20,6 @@ pub enum UsdtError { PendingTransfer, #[error("The selected USDT0 route is unavailable")] UnsupportedRoute, - #[error("This deposit needs provider assistance. Check its recovery status")] - DepositNeedsAttention, #[error("USDT payments are not configured for this app build")] NotConfigured, #[error("The network could not be reached. Try again")] diff --git a/src/modules/usdt/fixtures/deposit-signature.json b/src/modules/usdt/fixtures/deposit-signature.json deleted file mode 100644 index 66fa60b..0000000 --- a/src/modules/usdt/fixtures/deposit-signature.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "request": "{\"owner\":\"0x9858EfFD232B4033E47d90003D41EC34EcaEda94\",\"payload\":{\"action\":\"receive\",\"amount\":\"100000000\",\"network\":\"ethereum\"},\"timestamp\":1800000000}", - "signature": "0xe4a2d8340e92339d4f0627a6bf6a9bdaaae55498726ed75155a1d891761cd8cc23d6555526f69ce111c584dad43fbd974669dd9778f69c75c1b869065e05a0381b" -} diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index 274ab58..b0c2037 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -1,9 +1,9 @@ use super::{ account::{SimpleAccount, ENTRY_POINT}, amount::token_amount, - transaction::{event_data, BridgeHelper, EntryPoint, Erc20}, - types::{BRIDGE_HELPER, EXPLORER, OFT, TOKEN}, - UsdtDestination, UsdtError, UsdtTransfer, UsdtTransferStatus, UsdtWallet, + transaction::{event_data, EntryPoint, Erc20}, + types::{EXPLORER, TOKEN}, + UsdtError, UsdtTransfer, UsdtTransferStatus, UsdtWallet, }; use alloy_primitives::{Address, Bytes, U256}; use alloy_sol_types::{SolCall, SolEvent}; @@ -233,9 +233,7 @@ impl UsdtWallet { id: format!("{hash}:{index}"), tx_hash: hash.into(), user_operation_hash: None, - bridge_guid: None, recipient: event.to.to_checksum(None), - destination: UsdtDestination::Arbitrum, amount: token_amount(event.value)?, received_amount: token_amount(event.value)?, fee: None, @@ -266,8 +264,8 @@ impl UsdtWallet { }; for (event, saved) in owned_operations { let operation_hash = format!("{:#x}", event.userOpHash); - let (recipient, amount, destination) = if let Some(saved) = saved { - (saved.recipient, saved.amount, saved.destination) + let (recipient, amount) = if let Some(saved) = saved { + (saved.recipient, saved.amount) } else { let Some(op) = batch.as_ref().and_then(|batch| { batch @@ -277,18 +275,16 @@ impl UsdtWallet { }) else { continue; }; - let Some((recipient, amount, destination)) = decode_payment(&op.callData)? else { + let Some((recipient, amount)) = decode_payment(&op.callData)? else { continue; }; - (recipient.to_checksum(None), amount, destination) + (recipient.to_checksum(None), amount) }; let mut transfer = UsdtTransfer { id: operation_hash.clone(), tx_hash: hash.into(), user_operation_hash: Some(operation_hash), - bridge_guid: None, recipient, - destination, amount, received_amount: amount, fee: None, @@ -313,7 +309,7 @@ impl UsdtWallet { } } -fn decode_payment(data: &[u8]) -> Result, UsdtError> { +fn decode_payment(data: &[u8]) -> Result, UsdtError> { let Ok(calls) = decode_calls(data) else { return Ok(None); }; @@ -324,40 +320,10 @@ fn decode_payment(data: &[u8]) -> Result if target == TOKEN { if let Ok(call) = Erc20::transferCall::abi_decode(&data) { payment_count += 1; - payment = Some(( - call.recipient, - token_amount(call.amount)?, - UsdtDestination::Arbitrum, - )); + payment = Some((call.recipient, token_amount(call.amount)?)); } else if Erc20::approveCall::abi_decode(&data).is_err() { supported = false; } - } else if target == BRIDGE_HELPER { - if let Ok(call) = BridgeHelper::sendCall::abi_decode(&data) { - if call.oft != OFT { - supported = false; - continue; - } - let Some(destination) = [ - UsdtDestination::Ethereum, - UsdtDestination::Polygon, - UsdtDestination::Plasma, - UsdtDestination::Stable, - ] - .into_iter() - .find(|d| d.endpoint() == Some(call.param.dstEid)) else { - supported = false; - continue; - }; - payment_count += 1; - payment = Some(( - Address::from_word(call.param.to), - token_amount(call.param.amountLD)?, - destination, - )); - } else { - supported = false; - } } else { supported = false; } diff --git a/src/modules/usdt/mod.rs b/src/modules/usdt/mod.rs index f324cbf..9a56f06 100644 --- a/src/modules/usdt/mod.rs +++ b/src/modules/usdt/mod.rs @@ -1,6 +1,5 @@ mod account; mod amount; -mod deposits; mod errors; mod history; mod keys; @@ -14,7 +13,6 @@ mod user_operation; mod wallet; pub use amount::{usdt_format_amount, usdt_parse_amount}; -pub use deposits::*; pub use errors::UsdtError; pub use keys::usdt_address; pub use payment_request::usdt_parse_payment_request; diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs index 528af37..72fafdb 100644 --- a/src/modules/usdt/paymaster.rs +++ b/src/modules/usdt/paymaster.rs @@ -273,13 +273,6 @@ fn approval_margin(value: U256) -> Result { .ok_or(UsdtError::InvalidResponse) } -pub(super) fn with_margin(value: U256) -> Result { - value - .checked_add(value / U256::from(5)) - .and_then(|value| value.checked_add(U256::from(1))) - .ok_or(UsdtError::InvalidResponse) -} - struct Terms { exchange_rate: U256, post_op_gas: U256, diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs index 35a3cee..c24fb09 100644 --- a/src/modules/usdt/rpc.rs +++ b/src/modules/usdt/rpc.rs @@ -1,4 +1,4 @@ -use super::{UsdtError, UsdtTransfer, UsdtTransferStatus}; +use super::UsdtError; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_sol_types::SolCall; use serde::{de::DeserializeOwned, Deserialize}; @@ -18,8 +18,6 @@ pub(super) struct Block { pub(super) struct Rpc { client: reqwest::Client, - #[cfg(test)] - pub(super) bridge_status_url: Option, url: String, chain_id: u64, next_request: Arc>, @@ -41,8 +39,6 @@ impl Rpc { let client = endpoint_client(&url, Duration::from_secs(25))?; Ok(Self { client, - #[cfg(test)] - bridge_status_url: None, url, chain_id, next_request: Arc::new(Mutex::new(Instant::now())), @@ -141,69 +137,6 @@ impl Rpc { *next = scheduled + REQUEST_INTERVAL; } - pub async fn bridge_status( - &self, - transfer: &UsdtTransfer, - ) -> Result { - if transfer.bridge_guid.is_none() { - return Ok(transfer.status); - } - let url = format!( - "https://scan.layerzero-api.com/v1/messages/tx/{}", - transfer.tx_hash - ); - #[cfg(test)] - let url = self - .bridge_status_url - .as_ref() - .map(|base| format!("{base}/{}", transfer.tx_hash)) - .unwrap_or(url); - let response = self - .client - .get(url) - .send() - .await - .map_err(|_| UsdtError::NetworkUnavailable)? - .error_for_status() - .map_err(|_| UsdtError::NetworkUnavailable)?; - let response = bounded_json(response, 2_097_152, UsdtError::InvalidResponse).await?; - let messages = response["data"] - .as_array() - .ok_or(UsdtError::InvalidResponse)?; - let message = messages.iter().find(|message| { - message["guid"].as_str().is_some_and(|guid| { - transfer - .bridge_guid - .as_ref() - .is_some_and(|expected| guid.eq_ignore_ascii_case(expected)) - }) && message["pathway"]["srcEid"].as_u64() == Some(30110) - && message["pathway"]["dstEid"].as_u64() - == transfer.destination.endpoint().map(u64::from) - && message["pathway"]["sender"]["address"] - .as_str() - .is_some_and(|a| a.eq_ignore_ascii_case(&super::types::OFT.to_string())) - && message["source"]["tx"]["txHash"] - .as_str() - .is_some_and(|h| h.eq_ignore_ascii_case(&transfer.tx_hash)) - }); - Ok(match message.and_then(|m| m["status"]["name"].as_str()) { - Some("DELIVERED") => UsdtTransferStatus::Confirmed, - Some( - "FAILED" - | "BLOCKED" - | "PAYLOAD_STORED" - | "APPLICATION_BURNED" - | "APPLICATION_SKIPPED", - ) => UsdtTransferStatus::BridgeNeedsAttention, - _ => transfer.status, - }) - } - - pub async fn balance(&self, address: Address) -> Result { - self.call("eth_getBalance", json!([address, "pending"])) - .await - } - pub async fn block(&self, number: u64) -> Result { self.call("eth_getBlockByNumber", json!([U256::from(number), false])) .await diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs index a124d32..6c613eb 100644 --- a/src/modules/usdt/store.rs +++ b/src/modules/usdt/store.rs @@ -135,8 +135,6 @@ impl Store { UsdtTransferStatus::Confirmed | UsdtTransferStatus::Failed | UsdtTransferStatus::Replaced - | UsdtTransferStatus::Bridging - | UsdtTransferStatus::BridgeNeedsAttention ); let mut connection = self.connection()?; let tx = connection.transaction()?; @@ -264,16 +262,6 @@ impl Store { if let Some(data) = existing { let saved: UsdtTransfer = decode(&data)?; transfer.id = saved.id; - if saved.tx_hash == transfer.tx_hash - && saved.bridge_guid == transfer.bridge_guid - && transfer.status == UsdtTransferStatus::Bridging - && matches!( - saved.status, - UsdtTransferStatus::Confirmed | UsdtTransferStatus::BridgeNeedsAttention - ) - { - transfer.status = saved.status; - } tx.execute( "UPDATE usdt_transfers SET data=?1, raw=NULL WHERE id=?2", params![serde_json::to_string(&transfer)?, transfer.id], @@ -299,12 +287,7 @@ impl Store { let mut result = Vec::new(); for row in rows { let transfer: UsdtTransfer = decode(&row?)?; - if matches!( - transfer.status, - UsdtTransferStatus::Pending - | UsdtTransferStatus::Bridging - | UsdtTransferStatus::BridgeNeedsAttention - ) { + if transfer.status == UsdtTransferStatus::Pending { result.push(transfer); } } diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index b614ef7..0b2984f 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -230,7 +230,6 @@ struct ChainState { reject_broadcast: bool, delay_gas_estimate: bool, paymaster: alloy_primitives::Address, - helper_balance: alloy_primitives::U256, history_input: Option, history_target: Option, receipt_logs: Option>, @@ -279,7 +278,6 @@ impl MockChain { reject_broadcast: false, delay_gas_estimate: false, paymaster: paymaster::PAYMASTER, - helper_balance: U256::from(1_000_000_000_000_000u64), history_input: None, history_target: Some(account::ENTRY_POINT), receipt_logs: None, @@ -391,11 +389,9 @@ impl ChainState { } "eth_getCode" => json!(self.account_code), "eth_getTransactionCount" => json!(U256::from(self.authorization_nonce)), - "eth_getBalance" => json!(self.helper_balance), "eth_call" => { let data: Bytes = serde_json::from_value(body["params"][0]["data"].clone()).unwrap(); - use transaction::{BridgeHelper, MessagingFee, OFTLimit, OFTReceipt, Oft}; let encoded = if data.starts_with(&transaction::EntryPoint::getNonceCall::SELECTOR) { let block = serde_json::from_value::(body["params"][1].clone()).ok(); @@ -409,37 +405,6 @@ impl ChainState { self.nonce }; U256::from(nonce).abi_encode() - } else if data.starts_with(&Oft::tokenCall::SELECTOR) { - types::TOKEN.abi_encode() - } else if data.starts_with(&Oft::peersCall::SELECTOR) { - alloy_primitives::B256::repeat_byte(1).abi_encode() - } else if data.starts_with(&Oft::quoteOFTCall::SELECTOR) { - let param = Oft::quoteOFTCall::abi_decode(&data).unwrap().param; - ( - OFTLimit { - minAmountLD: U256::from(1), - maxAmountLD: U256::MAX, - }, - Vec::::new(), - OFTReceipt { - amountSentLD: param.amountLD, - amountReceivedLD: param.amountLD, - }, - ) - .abi_encode_params() - } else if data.starts_with(&Oft::quoteSendCall::SELECTOR) { - MessagingFee { - nativeFee: U256::from(10_000_000_000u64), - lzTokenFee: U256::ZERO, - } - .abi_encode() - } else if data.starts_with(&BridgeHelper::maxGasCall::SELECTOR) { - U256::from(1_000_000_000_000_000u64).abi_encode() - } else if data.starts_with(&BridgeHelper::quoteSendCall::SELECTOR) { - let param = BridgeHelper::quoteSendCall::abi_decode(&data) - .unwrap() - .param; - (param.amountLD + U256::from(300_000)).abi_encode() } else { self.balance.abi_encode() }; @@ -678,13 +643,13 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { let wallet = chain.wallet(&dir); let quote = tokio::time::timeout( std::time::Duration::from_secs(3), - wallet.quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum), + wallet.quote_transfer(RECIPIENT.into(), 1_000_000), ) .await .expect("An idle wallet must quote without a fixed per-request delay") .unwrap(); let next = wallet - .quote_transfer(RECIPIENT.into(), 2_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 2_000_000) .await .unwrap(); chain.state.lock().unwrap().reject_broadcast = true; @@ -717,9 +682,7 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { // A pending payment is rejected locally even if the provider is now misconfigured. chain.state.lock().unwrap().chain = 1; assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 2_000_000, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 2_000_000).await, Err(UsdtError::PendingTransfer) )); assert!(matches!( @@ -753,7 +716,7 @@ async fn fluctuating_gas_estimates_preserve_the_reviewed_operation() { chain.state.lock().unwrap().pre_verification_estimates = [80_000, 80_100, 80_200, 80_300].into(); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let reviewed = wallet.store.quote("e.id).unwrap().plan.operation; @@ -793,14 +756,12 @@ async fn wrong_network_owner_nonce_balance_and_paymaster_cannot_sign() { types::TOKEN.to_checksum(None) ); assert!(matches!( - wallet - .quote_transfer(request, 1_000_000, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(request, 1_000_000).await, Err(UsdtError::InvalidAddress) )); } let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); assert!(matches!( @@ -842,9 +803,7 @@ async fn wrong_network_owner_nonce_balance_and_paymaster_cannot_sign() { state.paymaster = Address::repeat_byte(1); } assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, Err(UsdtError::UnsupportedRoute) )); assert!(chain.state.lock().unwrap().operations.is_empty()); @@ -856,7 +815,7 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); wallet @@ -874,9 +833,7 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { UsdtTransferStatus::Pending ); assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, Err(UsdtError::PendingTransfer) )); chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(421); @@ -885,7 +842,7 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { UsdtTransferStatus::Failed ); let next = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); assert_eq!( @@ -898,63 +855,13 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { ); } -#[tokio::test] -async fn bridge_payment_bounds_token_fees_and_revokes_helper_approval() { - use alloy_primitives::U256; - use alloy_sol_types::SolCall; - let chain = MockChain::start().await; - let dir = tempfile::tempdir().unwrap(); - let wallet = chain.wallet(&dir); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) - .await - .unwrap(); - let plan = wallet.store.quote("e.id).unwrap().plan; - let calls = history::decode_calls(&plan.operation.call_data).unwrap(); - assert_eq!(calls.len(), 4); - let paymaster = transaction::Erc20::approveCall::abi_decode(&calls[0].1).unwrap(); - assert_eq!(calls[0].0, types::TOKEN); - assert_eq!(paymaster.spender, paymaster::PAYMASTER); - let approval = transaction::Erc20::approveCall::abi_decode(&calls[1].1).unwrap(); - assert_eq!(calls[1].0, types::TOKEN); - assert_eq!(approval.spender, types::BRIDGE_HELPER); - assert_eq!(approval.amount, U256::from(1_360_001)); - let send = transaction::BridgeHelper::sendCall::abi_decode(&calls[2].1).unwrap(); - assert_eq!(calls[2].0, types::BRIDGE_HELPER); - assert_eq!(send.oft, types::OFT); - assert_eq!(send.param.dstEid, 30109); - assert_eq!( - send.param.to, - RECIPIENT - .parse::() - .unwrap() - .into_word() - ); - assert_eq!(send.param.minAmountLD, U256::from(1_000_000)); - assert_eq!(send.fee.nativeFee, U256::from(10_000_000_000u64)); - let revoke = transaction::Erc20::approveCall::abi_decode(&calls[3].1).unwrap(); - assert_eq!(revoke.spender, types::BRIDGE_HELPER); - assert!(revoke.amount.is_zero()); - let bridge_fee = approval.amount.to::() - quote.amount; - assert_eq!(bridge_fee, 360_001); - assert!(paymaster.amount > U256::from(quote.maximum_fee - bridge_fee)); - assert_eq!(quote.received_amount, 1_000_000); - chain.state.lock().unwrap().helper_balance = U256::ZERO; - assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) - .await, - Err(UsdtError::UnsupportedRoute) - )); -} - #[tokio::test] async fn seed_restore_recovers_mined_payments_without_local_submission_data() { let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -982,7 +889,7 @@ async fn seed_restore_recovers_mined_payments_without_local_submission_data() { } #[tokio::test] -async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_fee() { +async fn bundled_operations_cannot_contribute_another_payments_fee() { use alloy_primitives::{B256, U256}; use alloy_sol_types::SolEvent; use serde_json::json; @@ -990,7 +897,7 @@ async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_ let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let mut transfer = wallet @@ -1004,7 +911,6 @@ async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_ .parse() .unwrap(); let other_hash = B256::repeat_byte(2); - let guid = B256::repeat_byte(3); let event = |hash| { transaction::EntryPoint::UserOperationEvent { userOpHash: hash, @@ -1019,8 +925,7 @@ async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_ }; let log = |address, data: alloy_primitives::LogData| json!({"address":address,"topics":data.topics(),"data":data.data}); let receipt = json!({"logs":[ - log(types::OFT, transaction::Oft::OFTSent { guid, dstEid:30109, fromAddress:types::BRIDGE_HELPER, amountSentLD:U256::from(1_000_000), amountReceivedLD:U256::from(1_000_000) }.encode_log_data()), - log(types::BRIDGE_HELPER, transaction::BridgeHelper::LogSend { sender:wallet.address, oft:types::OFT, amountLD:U256::from(1_000_000), nativeFee:U256::from(100), feeInToken:U256::from(500), totalAmount:U256::from(1_000_500) }.encode_log_data()), + log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:other_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(500), exchangeRate:U256::from(1) }.encode_log_data()), log(account::ENTRY_POINT, event(other_hash)), log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:own_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(123), exchangeRate:U256::from(1) }.encode_log_data()), log(account::ENTRY_POINT, event(own_hash)), @@ -1028,25 +933,14 @@ async fn bundled_operations_cannot_contribute_another_payments_bridge_status_or_ wallet.settle(&mut transfer, &receipt, false).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Failed); assert_eq!(transfer.fee, Some(123)); - assert_eq!(transfer.bridge_guid, None); wallet.settle(&mut transfer, &receipt, true).unwrap(); - assert_eq!(transfer.status, UsdtTransferStatus::BridgeNeedsAttention); - assert_eq!(transfer.bridge_guid, None); + assert_eq!(transfer.status, UsdtTransferStatus::Confirmed); assert_eq!(transfer.fee, Some(123)); - - let mut receipt = receipt; - let bridge_log = receipt["logs"][1].clone(); - receipt["logs"] - .as_array_mut() - .unwrap() - .insert(4, bridge_log); - wallet.settle(&mut transfer, &receipt, true).unwrap(); - assert_eq!(transfer.fee, Some(623)); } #[tokio::test] #[ignore = "requires a fresh local Arbitrum fork and tests/usdt-fork/provider.mjs"] -async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomically() { +async fn deployed_contracts_collect_usdt_fees_without_account_eth() { use alloy_primitives::U256; use serde_json::json; let rpc = rpc::Rpc::new("http://127.0.0.1:18545".into(), types::CHAIN_ID).unwrap(); @@ -1060,7 +954,12 @@ async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomical std::env::var("USDT_FORK_BUNDLER_URL").unwrap_or_else(|_| "http://127.0.0.1:18546".into()), ) .unwrap(); - assert_eq!(rpc.balance(wallet.address).await.unwrap(), U256::ZERO); + assert_eq!( + rpc.call::("eth_getBalance", json!([wallet.address, "pending"])) + .await + .unwrap(), + U256::ZERO + ); let initial = wallet.balance().await.unwrap(); // Only locally mined transactions belong to this fixture's history. wallet @@ -1068,7 +967,7 @@ async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomical .complete_history(wallet.block_number().await.unwrap()) .unwrap(); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -1081,67 +980,12 @@ async fn deployed_contracts_collect_usdt_fees_and_revert_failed_bridges_atomical let fee = transfer.fee.unwrap(); assert!(fee > 0 && fee <= quote.maximum_fee); assert_eq!(wallet.balance().await.unwrap(), initial - 1_000_000 - fee); - - let bridge = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) - .await - .unwrap(); - let helper_balance = rpc.balance(types::BRIDGE_HELPER).await.unwrap(); - let _: serde_json::Value = rpc - .call("anvil_setBalance", json!([types::BRIDGE_HELPER, "0x0"])) - .await - .unwrap(); - let before = wallet.balance().await.unwrap(); - let sent = wallet - .send(bridge.id, TEST_PHRASE.into(), None) - .await - .unwrap(); - let history = wallet.refresh_transfers().await.unwrap(); - let failed = history.iter().find(|t| t.id == sent.id).unwrap(); - assert_eq!(failed.status, UsdtTransferStatus::Failed); - assert!(failed.bridge_guid.is_none()); - assert!(before - wallet.balance().await.unwrap() <= bridge.maximum_fee); - let _: serde_json::Value = rpc - .call( - "anvil_setBalance", - json!([types::BRIDGE_HELPER, helper_balance]), - ) - .await - .unwrap(); - - let bridge = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Polygon) - .await - .unwrap(); - let before = wallet.balance().await.unwrap(); - let sent = wallet - .send(bridge.id, TEST_PHRASE.into(), None) - .await - .unwrap(); - let history = wallet.refresh_transfers().await.unwrap(); - let bridged = history.iter().find(|t| t.id == sent.id).unwrap(); - assert_eq!(bridged.status, UsdtTransferStatus::Bridging); - assert!(bridged.bridge_guid.is_some()); - assert!(bridged.fee.unwrap() > 0); - assert!(bridged.fee.unwrap() <= bridge.maximum_fee); - sync_history_to_tip(&wallet).await; assert_eq!( - wallet.balance().await.unwrap(), - before - 1_000_000 - bridged.fee.unwrap() + rpc.call::("eth_getBalance", json!([wallet.address, "pending"])) + .await + .unwrap(), + U256::ZERO ); - alloy_sol_types::sol! { function allowance(address owner, address spender) view returns (uint256); } - let allowance = rpc - .contract( - types::TOKEN, - allowanceCall { - owner: wallet.address, - spender: types::BRIDGE_HELPER, - }, - ) - .await - .unwrap(); - assert!(allowance.is_zero()); - assert_eq!(rpc.balance(wallet.address).await.unwrap(), U256::ZERO); } #[tokio::test] @@ -1152,7 +996,7 @@ async fn history_preserves_receipts_with_external_account_call_shapes() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); wallet @@ -1215,7 +1059,7 @@ async fn wrapped_history_preserves_signed_payments_and_restores_token_transfers( let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -1312,9 +1156,7 @@ fn stored_activity_is_complete_and_sorted_newest_first() { id: format!("receipt-{index}"), tx_hash: format!("tx-{index}"), user_operation_hash: None, - bridge_guid: None, recipient: RECIPIENT.into(), - destination: UsdtDestination::Arbitrum, amount: 1, received_amount: 1, fee: None, @@ -1337,20 +1179,14 @@ async fn nonce_advance_with_delayed_logs_remains_pending_and_history_reconciles( let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); - for (recipient, destination) in [ - (wallet.receive_address(), UsdtDestination::Arbitrum), - (wallet.receive_address(), UsdtDestination::Ethereum), - (types::TOKEN.to_checksum(None), UsdtDestination::Arbitrum), - ] { + for recipient in [wallet.receive_address(), types::TOKEN.to_checksum(None)] { assert!(matches!( - wallet - .quote_transfer(recipient, 1_000_000, destination) - .await, + wallet.quote_transfer(recipient, 1_000_000).await, Err(UsdtError::InvalidAddress) )); } let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -1374,9 +1210,7 @@ async fn nonce_advance_with_delayed_logs_remains_pending_and_history_reconciles( ); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, Err(UsdtError::PendingTransfer) )); { @@ -1399,7 +1233,7 @@ async fn interrupted_history_resumes_without_repeating_completed_work() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); wallet @@ -1452,7 +1286,7 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); chain.state.lock().unwrap().reject_broadcast = true; @@ -1485,7 +1319,7 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { ); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); assert_eq!( @@ -1574,7 +1408,7 @@ async fn quote_expiring_during_validation_is_not_signed() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let mut data = wallet.store.quote("e.id).unwrap(); @@ -1600,31 +1434,6 @@ async fn sync_history_to_tip(wallet: &UsdtWallet) { .expect("History must make progress within successive bounded scans"); } -#[tokio::test] -async fn deposit_http_throttling_is_distinct_from_network_failure() { - use tokio::io::{AsyncReadExt, AsyncWriteExt}; - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let url = format!("http://{}", listener.local_addr().unwrap()); - let server = tokio::spawn(async move { - let (mut socket, _) = listener.accept().await.unwrap(); - let mut request = [0u8; 4096]; - assert!(socket.read(&mut request).await.unwrap() > 0); - socket - .write_all( - b"HTTP/1.1 429 Too Many Requests\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", - ) - .await - .unwrap(); - }); - let client = - UsdtDepositClient::new(usdt_address(TEST_PHRASE.into(), None).unwrap(), url).unwrap(); - assert!(matches!( - client.networks().await, - Err(UsdtError::RateLimited) - )); - server.await.unwrap(); -} - #[tokio::test] async fn invalid_chain_data_and_stored_json_have_distinct_errors() { let chain = MockChain::start().await; @@ -1654,151 +1463,6 @@ async fn invalid_chain_data_and_stored_json_have_distinct_errors() { )); } -#[tokio::test] -async fn stalled_bridge_status_checks_leave_time_for_source_recovery_and_sending() { - use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }; - use tokio::{ - io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, - time::Duration, - }; - let chain = MockChain::start().await; - let directory = tempfile::tempdir().unwrap(); - let mut wallet = chain.wallet(&directory); - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let address = listener.local_addr().unwrap(); - let attempts = Arc::new(Mutex::new(Vec::new())); - let stalled = Arc::new(AtomicBool::new(true)); - let accepted = attempts.clone(); - let stalled_server = stalled.clone(); - let server = tokio::spawn(async move { - let mut requests = tokio::task::JoinSet::new(); - while let Ok((socket, _)) = listener.accept().await { - let accepted = accepted.clone(); - let stalled = stalled_server.load(Ordering::SeqCst); - requests.spawn(async move { - let mut reader = BufReader::new(socket); - let mut line = String::new(); - reader.read_line(&mut line).await.unwrap(); - let hash = line.split_whitespace().nth(1).unwrap().trim_start_matches('/').to_string(); - loop { - line.clear(); - if reader.read_line(&mut line).await.unwrap() == 0 || line == "\r\n" { break; } - } - accepted.lock().unwrap().push(hash.clone()); - tokio::time::sleep(if stalled { Duration::from_secs(25) } else { Duration::from_millis(1200) }).await; - let index = hash.strip_prefix("bridge-tx-").unwrap(); - let body = serde_json::json!({"data":[{ - "guid":format!("guid-{index}"), - "pathway":{"srcEid":30110,"dstEid":30109,"sender":{"address":types::OFT}}, - "source":{"tx":{"txHash":hash}},"status":{"name":"DELIVERED"} - }]}).to_string(); - let response = format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", body.len(), body); - let _ = reader.into_inner().write_all(response.as_bytes()).await; - }); - } - }); - Arc::get_mut(&mut wallet).unwrap().rpc.bridge_status_url = Some(format!("http://{address}")); - let bridges: Vec<_> = (0..5) - .map(|index| UsdtTransfer { - id: format!("bridge-{index}"), - tx_hash: format!("bridge-tx-{index}"), - user_operation_hash: None, - bridge_guid: Some(format!("guid-{index}")), - recipient: RECIPIENT.into(), - destination: UsdtDestination::Polygon, - amount: 1_000_000, - received_amount: 1_000_000, - fee: Some(20), - is_incoming: false, - status: UsdtTransferStatus::Bridging, - timestamp: 1, - explorer_url: String::new(), - }) - .collect(); - wallet - .store - .save_history_receipt(&bridges, "bridges") - .unwrap(); - // A signed operation with no nonce consumption must still resolve once expired. - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) - .await - .unwrap(); - chain.state.lock().unwrap().reject_broadcast = true; - let sent = wallet - .send(quote.id, TEST_PHRASE.into(), None) - .await - .unwrap(); - { - let mut state = chain.state.lock().unwrap(); - state.tip += 3; - state.timestamp += alloy_primitives::U256::from(601); - } - // Completion includes bridge polling and the shared chain/bundler request budget. - let history = tokio::time::timeout(Duration::from_secs(15), wallet.refresh_transfers()) - .await - .unwrap() - .unwrap(); - assert_eq!(attempts.lock().unwrap().len(), 1); - assert_eq!( - history - .iter() - .find(|transfer| transfer.id == sent.id) - .unwrap() - .status, - UsdtTransferStatus::Failed - ); - assert_eq!( - history - .iter() - .filter(|transfer| transfer.status == UsdtTransferStatus::Bridging) - .count(), - 5 - ); - chain.state.lock().unwrap().reject_broadcast = false; - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) - .await - .unwrap(); - // Hold the refresh lock before send, while all bridge status connections stall again. - let refresh_wallet = wallet.clone(); - let refresh = tokio::spawn(async move { refresh_wallet.refresh_transfers().await }); - tokio::time::timeout(Duration::from_secs(3), async { - while attempts.lock().unwrap().len() < 2 { - tokio::time::sleep(Duration::from_millis(10)).await; - } - }) - .await - .unwrap(); - let result = tokio::time::timeout( - Duration::from_secs(15), - wallet.send(quote.id, TEST_PHRASE.into(), None), - ) - .await; - refresh.await.unwrap().unwrap(); - assert_eq!(result.unwrap().unwrap().status, UsdtTransferStatus::Pending); - { - let attempts = attempts.lock().unwrap(); - assert_ne!(attempts[0], attempts[1]); - } - // Healthy responses slower than an equal share of the budget must still settle. - stalled.store(false, Ordering::SeqCst); - for _ in 0..2 { - tokio::time::timeout(Duration::from_secs(15), wallet.refresh_transfers()) - .await - .unwrap() - .unwrap(); - } - let history = wallet.history().unwrap(); - assert!(bridges.iter().all(|bridge| history.iter().any( - |transfer| transfer.id == bridge.id && transfer.status == UsdtTransferStatus::Confirmed - ))); - server.abort(); -} - #[tokio::test] async fn each_payment_authorizes_the_current_nonce_after_delegation() { use alloy_primitives::{Bytes, U256}; @@ -1806,7 +1470,7 @@ async fn each_payment_authorizes_the_current_nonce_after_delegation() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); wallet @@ -1827,7 +1491,7 @@ async fn each_payment_authorizes_the_current_nonce_after_delegation() { UsdtTransferStatus::Confirmed ); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); wallet @@ -1850,7 +1514,7 @@ async fn consumed_authorization_preserves_pending_payment_until_signed_expiry() let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let quote_data = wallet.store.quote("e.id).unwrap(); @@ -1864,7 +1528,7 @@ async fn consumed_authorization_preserves_pending_payment_until_signed_expiry() )); assert!(chain.state.lock().unwrap().operations.is_empty()); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); chain.state.lock().unwrap().reject_broadcast = true; @@ -1898,9 +1562,7 @@ async fn consumed_authorization_preserves_pending_payment_until_signed_expiry() assert_eq!(retained.operation.eip7702_auth, signed.eip7702_auth); assert_eq!(retained.operation.signature, signed.signature); assert!(matches!( - restored - .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) - .await, + restored.quote_transfer(RECIPIENT.into(), 1).await, Err(UsdtError::PendingTransfer) )); chain.state.lock().unwrap().timestamp = U256::from(retained.expires_at + 1); @@ -1908,10 +1570,7 @@ async fn consumed_authorization_preserves_pending_payment_until_signed_expiry() assert_eq!(history[0].status, UsdtTransferStatus::Failed); assert_eq!(history[0].fee, Some(0)); assert!(restored.store.pending_plan(&sent.id).unwrap().is_none()); - let quote = restored - .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) - .await - .unwrap(); + let quote = restored.quote_transfer(RECIPIENT.into(), 1).await.unwrap(); assert_eq!( restored .store @@ -1933,9 +1592,7 @@ async fn foreign_delegation_and_unbounded_paymaster_terms_cannot_authorize_payme let wallet = chain.wallet(&dir); chain.state.lock().unwrap().account_code = Bytes::from_static(&[0xef, 0x01, 0x00, 1]); assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 1).await, Err(UsdtError::UnsupportedDelegation) )); assert_eq!(wallet.balance().await.unwrap(), 10_000_000); @@ -1945,17 +1602,12 @@ async fn foreign_delegation_and_unbounded_paymaster_terms_cannot_authorize_payme for expiry in [0, timestamp + 901] { chain.state.lock().unwrap().paymaster_valid_until = Some(expiry); assert!(matches!( - wallet - .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) - .await, + wallet.quote_transfer(RECIPIENT.into(), 1).await, Err(UsdtError::InvalidResponse) )); } chain.state.lock().unwrap().paymaster_valid_until = Some(timestamp + 900); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1, UsdtDestination::Arbitrum) - .await - .unwrap(); + let quote = wallet.quote_transfer(RECIPIENT.into(), 1).await.unwrap(); chain.state.lock().unwrap().account_code = Bytes::from( [ &[0xef, 0x01, 0x00][..], @@ -1978,10 +1630,7 @@ async fn seed_restore_includes_external_token_sends_without_duplicate_operation_ let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 77, UsdtDestination::Arbitrum) - .await - .unwrap(); + let quote = wallet.quote_transfer(RECIPIENT.into(), 77).await.unwrap(); wallet .send(quote.id, TEST_PHRASE.into(), None) .await @@ -2024,7 +1673,7 @@ async fn gas_price_changes_require_a_new_quote_before_signing() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); chain.state.lock().unwrap().gas_price = 60_000_000; @@ -2043,7 +1692,7 @@ async fn consumed_nonce_recovery_requires_complete_receipts_and_resumes_after_re let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -2081,7 +1730,7 @@ async fn consumed_nonce_recovery_requires_complete_receipts_and_resumes_after_re 0 ); restored - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); } @@ -2092,7 +1741,7 @@ async fn consuming_block_receipts_recover_a_payment_hidden_from_log_queries() { let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet @@ -2119,7 +1768,7 @@ async fn dense_block_history_recovers_large_receipts_without_skipping_after_rest let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000, UsdtDestination::Arbitrum) + .quote_transfer(RECIPIENT.into(), 1_000_000) .await .unwrap(); let sent = wallet diff --git a/src/modules/usdt/transaction.rs b/src/modules/usdt/transaction.rs index 4d7c8d6..6b882b8 100644 --- a/src/modules/usdt/transaction.rs +++ b/src/modules/usdt/transaction.rs @@ -16,47 +16,6 @@ sol! { bytes paymasterAndData; bytes signature; } - #[derive(Debug)] - struct SendParam { - uint32 dstEid; - bytes32 to; - uint256 amountLD; - uint256 minAmountLD; - bytes extraOptions; - bytes composeMsg; - bytes oftCmd; - } - #[derive(Debug)] - struct MessagingFee { - uint256 nativeFee; - uint256 lzTokenFee; - } - struct OFTLimit { - uint256 minAmountLD; - uint256 maxAmountLD; - } - struct OFTFeeDetail { - int256 feeAmountLD; - string description; - } - struct OFTReceipt { - uint256 amountSentLD; - uint256 amountReceivedLD; - } - interface Oft { - function token() external view returns (address); - function peers(uint32 eid) external view returns (bytes32); - function quoteOFT(SendParam param) external view returns (OFTLimit limit, OFTFeeDetail[] fees, OFTReceipt receipt); - function quoteSend(SendParam param, bool payInLzToken) external view returns (MessagingFee fee); - event OFTSent(bytes32 indexed guid, uint32 dstEid, address indexed fromAddress, uint256 amountSentLD, uint256 amountReceivedLD); - } - interface BridgeHelper { - function token() external view returns (address); - function maxGas() external view returns (uint256); - function quoteSend(SendParam param, MessagingFee fee) external view returns (uint256 totalAmount); - function send(address oft, SendParam param, MessagingFee fee) external payable; - event LogSend(address indexed sender, address indexed oft, uint256 amountLD, uint256 nativeFee, uint256 feeInToken, uint256 totalAmount); - } interface EntryPoint { function getNonce(address sender, uint192 key) view returns (uint256); function handleOps(PackedOperation[] ops, address beneficiary); diff --git a/src/modules/usdt/types.rs b/src/modules/usdt/types.rs index 45245f3..744b696 100644 --- a/src/modules/usdt/types.rs +++ b/src/modules/usdt/types.rs @@ -3,31 +3,8 @@ use serde::{Deserialize, Serialize}; pub(super) const CHAIN_ID: u64 = 42161; pub(super) const TOKEN: Address = address!("Fd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"); -pub(super) const OFT: Address = address!("14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"); -pub(super) const BRIDGE_HELPER: Address = address!("a90f03c856D01F698E7071B393387cd75a8a319A"); pub(super) const EXPLORER: &str = "https://arbiscan.io"; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] -pub enum UsdtDestination { - Stable, - Ethereum, - Arbitrum, - Polygon, - Plasma, -} - -impl UsdtDestination { - pub(super) fn endpoint(self) -> Option { - match self { - Self::Stable => Some(30396), - Self::Ethereum => Some(30101), - Self::Arbitrum => None, - Self::Polygon => Some(30109), - Self::Plasma => Some(30383), - } - } -} - #[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] pub struct UsdtPaymentRequest { pub recipient: String, @@ -38,9 +15,7 @@ pub struct UsdtPaymentRequest { pub struct UsdtQuote { pub id: String, pub recipient: String, - pub destination: UsdtDestination, pub amount: u64, - pub received_amount: u64, pub maximum_fee: u64, pub expires_at: u64, } @@ -50,8 +25,6 @@ pub enum UsdtTransferStatus { Pending, Confirmed, Failed, - Bridging, - BridgeNeedsAttention, Replaced, } @@ -60,9 +33,7 @@ pub struct UsdtTransfer { pub id: String, pub tx_hash: String, pub user_operation_hash: Option, - pub bridge_guid: Option, pub recipient: String, - pub destination: UsdtDestination, pub amount: u64, pub received_amount: u64, pub fee: Option, diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 64ec4ab..1ff5049 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -2,23 +2,18 @@ use super::{ account::{validate_delegation, ENTRY_POINT}, amount::token_amount, keys::{derive_key, parse_address}, - paymaster::{with_margin, Pimlico, PAYMASTER}, + paymaster::{Pimlico, PAYMASTER}, rpc::Rpc, store::{QuoteData, Store}, - transaction::{ - event_data, BridgeHelper, EntryPoint, Erc20, MessagingFee, Oft, Paymaster, Plan, SendParam, - }, - types::{BRIDGE_HELPER, CHAIN_ID, EXPLORER, OFT, TOKEN}, + transaction::{event_data, EntryPoint, Erc20, Paymaster, Plan}, + types::{CHAIN_ID, EXPLORER, TOKEN}, user_operation::Authorization, - UsdtDestination, UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus, + UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus, }; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_sol_types::{SolCall, SolEvent}; use serde_json::{json, Value}; -use std::sync::{ - atomic::{AtomicU64, AtomicUsize, Ordering}, - Arc, -}; +use std::sync::{atomic::AtomicU64, Arc}; use tokio::sync::Mutex; #[derive(uniffi::Object)] @@ -28,7 +23,6 @@ pub struct UsdtWallet { pub(super) paymaster: Pimlico, pub(super) store: Store, operation: Mutex<()>, - bridge_poll_offset: AtomicUsize, pub(super) history_range_limit: AtomicU64, } @@ -56,7 +50,6 @@ impl UsdtWallet { paymaster, store, operation: Mutex::new(()), - bridge_poll_offset: AtomicUsize::new(0), history_range_limit: AtomicU64::new(super::history::MAX_LOG_RANGE), })) } @@ -82,45 +75,41 @@ impl UsdtWallet { &self, recipient: String, amount: u64, - destination: UsdtDestination, ) -> Result { if amount == 0 { return Err(UsdtError::InvalidAmount); } let recipient = parse_address(recipient.trim())?; - if recipient == self.address - || (destination == UsdtDestination::Arbitrum && recipient == TOKEN) - { + if recipient == self.address || recipient == TOKEN { return Err(UsdtError::InvalidAddress); } self.store.require_no_pending()?; self.rpc.verify_chain().await?; self.require_balance(amount, 0).await?; - let (calls, received_amount, bridge_fee) = - self.transfer_calls(recipient, amount, destination).await?; - if bridge_fee > 0 { - self.require_balance(amount, bridge_fee).await?; - } + let calls = vec![( + TOKEN, + Erc20::transferCall { + recipient, + amount: U256::from(amount), + } + .abi_encode() + .into(), + )]; let nonce = self.nonce("latest").await?; let authorization = self.authorization().await?; let created_block = self.block_number().await?; let timestamp = self.block_timestamp(created_block).await?; - let (operation, gas_fee, operation_expires_at) = self + let (operation, maximum_fee, operation_expires_at) = self .paymaster .prepare(self.address, nonce, authorization, &calls, timestamp) .await?; let expires_at = now().saturating_add(operation_expires_at.saturating_sub(timestamp).min(120)); - let maximum_fee = gas_fee - .checked_add(bridge_fee) - .ok_or(UsdtError::InvalidAmount)?; self.require_balance(amount, maximum_fee).await?; let quote = UsdtQuote { id: uuid::Uuid::new_v4().to_string(), recipient: recipient.to_checksum(None), - destination, amount, - received_amount, maximum_fee, expires_at, }; @@ -188,11 +177,9 @@ impl UsdtWallet { id: quote_id, tx_hash: String::new(), user_operation_hash: Some(format!("{hash:#x}")), - bridge_guid: None, recipient: data.quote.recipient, - destination: data.quote.destination, amount: data.quote.amount, - received_amount: data.quote.received_amount, + received_amount: data.quote.amount, fee: None, is_incoming: false, status: UsdtTransferStatus::Pending, @@ -222,14 +209,7 @@ impl UsdtWallet { return self.history(); } self.rpc.verify_chain().await?; - self.refresh_bridges(&transfers).await?; for mut transfer in transfers { - if matches!( - transfer.status, - UsdtTransferStatus::Bridging | UsdtTransferStatus::BridgeNeedsAttention - ) { - continue; - } let Some(plan) = self.store.pending_plan(&transfer.id)? else { continue; }; @@ -387,37 +367,6 @@ impl UsdtWallet { self.store.update_transfer(transfer) } - async fn refresh_bridges(&self, transfers: &[UsdtTransfer]) -> Result<(), UsdtError> { - let mut bridges: Vec<_> = transfers - .iter() - .filter(|transfer| { - matches!( - transfer.status, - UsdtTransferStatus::Bridging | UsdtTransferStatus::BridgeNeedsAttention - ) - }) - .collect(); - if bridges.is_empty() { - return Ok(()); - } - let offset = self.bridge_poll_offset.fetch_add(1, Ordering::Relaxed) % bridges.len(); - bridges.rotate_left(offset); - // Rotate the first check so stalled bridges cannot starve later status checks or sends. - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(5); - for transfer in bridges { - match tokio::time::timeout_at(deadline, self.rpc.bridge_status(transfer)).await { - Ok(Ok(status)) => { - let mut transfer = transfer.clone(); - transfer.status = status; - self.store.update_transfer(&transfer)?; - } - Ok(Err(_)) => {} - Err(_) => break, - } - } - Ok(()) - } - pub(super) async fn block_number(&self) -> Result { u64::try_from(self.rpc.call::("eth_blockNumber", json!([])).await?) .map_err(|_| UsdtError::InvalidResponse) @@ -548,7 +497,6 @@ impl UsdtWallet { .map_err(|_| UsdtError::InvalidResponse)?; let logs = super::transaction::operation_logs(receipt, operation_hash)?; let mut gas_fee = None; - let mut bridge_fee = None; for log in logs { let address: Address = serde_json::from_value(log["address"].clone())?; let data = event_data(log)?; @@ -563,174 +511,16 @@ impl UsdtWallet { } } } - if success && address == BRIDGE_HELPER { - if let Ok(event) = BridgeHelper::LogSend::decode_log_data(&data) { - if event.sender == self.address - && event.oft == OFT - && event.amountLD == U256::from(transfer.amount) - { - bridge_fee = Some(token_amount(event.feeInToken)?); - } - } - } - if success && address == OFT { - if let Ok(event) = Oft::OFTSent::decode_log_data(&data) { - if event.fromAddress == BRIDGE_HELPER - && transfer.destination.endpoint() == Some(event.dstEid) - && event.amountSentLD == U256::from(transfer.amount) - { - transfer.bridge_guid = Some(format!("{:#x}", event.guid)); - transfer.received_amount = token_amount(event.amountReceivedLD)?; - } - } - } } - transfer.fee = gas_fee.and_then(|fee| fee.checked_add(bridge_fee.unwrap_or(0))); + transfer.fee = gas_fee; transfer.status = if !success { transfer.received_amount = 0; UsdtTransferStatus::Failed - } else if transfer.destination == UsdtDestination::Arbitrum { - UsdtTransferStatus::Confirmed - } else if transfer.bridge_guid.is_some() { - UsdtTransferStatus::Bridging } else { - UsdtTransferStatus::BridgeNeedsAttention + UsdtTransferStatus::Confirmed }; Ok(()) } - async fn transfer_calls( - &self, - recipient: Address, - amount: u64, - destination: UsdtDestination, - ) -> Result<(Vec<(Address, Bytes)>, u64, u64), UsdtError> { - let Some(eid) = destination.endpoint() else { - return Ok(( - vec![( - TOKEN, - Erc20::transferCall { - recipient, - amount: U256::from(amount), - } - .abi_encode() - .into(), - )], - amount, - 0, - )); - }; - let token = self.rpc.contract(OFT, Oft::tokenCall {}).await?; - let helper_token = self - .rpc - .contract(BRIDGE_HELPER, BridgeHelper::tokenCall {}) - .await?; - let peer = self.rpc.contract(OFT, Oft::peersCall { eid }).await?; - if token != TOKEN || helper_token != TOKEN || peer.is_zero() { - return Err(UsdtError::UnsupportedRoute); - } - let mut param = SendParam { - dstEid: eid, - to: recipient.into_word(), - amountLD: U256::from(amount), - minAmountLD: U256::ZERO, - extraOptions: Bytes::new(), - composeMsg: Bytes::new(), - oftCmd: Bytes::new(), - }; - let oft = self - .rpc - .contract( - OFT, - Oft::quoteOFTCall { - param: param.clone(), - }, - ) - .await?; - if U256::from(amount) < oft.limit.minAmountLD - || U256::from(amount) > oft.limit.maxAmountLD - || oft.receipt.amountSentLD != U256::from(amount) - || oft.receipt.amountReceivedLD.is_zero() - || oft.receipt.amountReceivedLD > U256::from(amount) - { - return Err(UsdtError::InvalidAmount); - } - param.minAmountLD = oft.receipt.amountReceivedLD; - let fee = self - .rpc - .contract( - OFT, - Oft::quoteSendCall { - param: param.clone(), - payInLzToken: false, - }, - ) - .await?; - let maximum_native = self - .rpc - .contract(BRIDGE_HELPER, BridgeHelper::maxGasCall {}) - .await?; - if !fee.lzTokenFee.is_zero() - || fee.nativeFee > maximum_native - || self.rpc.balance(BRIDGE_HELPER).await? < fee.nativeFee - { - return Err(UsdtError::UnsupportedRoute); - } - let total = self - .rpc - .contract( - BRIDGE_HELPER, - BridgeHelper::quoteSendCall { - param: param.clone(), - fee: fee.clone(), - }, - ) - .await?; - let fee = MessagingFee { - nativeFee: fee.nativeFee, - lzTokenFee: U256::ZERO, - }; - let token_fee = total - .checked_sub(U256::from(amount)) - .ok_or(UsdtError::InvalidResponse)?; - let token_fee = with_margin(token_fee)?; - let approval = U256::from(amount) - .checked_add(token_fee) - .ok_or(UsdtError::InvalidResponse)?; - Ok(( - vec![ - ( - TOKEN, - Erc20::approveCall { - spender: BRIDGE_HELPER, - amount: approval, - } - .abi_encode() - .into(), - ), - ( - BRIDGE_HELPER, - BridgeHelper::sendCall { - oft: OFT, - param, - fee, - } - .abi_encode() - .into(), - ), - ( - TOKEN, - Erc20::approveCall { - spender: BRIDGE_HELPER, - amount: U256::ZERO, - } - .abi_encode() - .into(), - ), - ], - token_amount(oft.receipt.amountReceivedLD)?, - token_amount(token_fee)?, - )) - } } pub(super) fn now() -> u64 { From 98128f2b349b4945ec9b7ac8e7f091f746deb31f Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 24 Sep 2026 22:20:53 +0300 Subject: [PATCH 5/9] fix: harden USDT submission and history recovery --- AGENTS.md | 4 +- Package.swift | 2 +- src/modules/usdt/README.md | 6 +- src/modules/usdt/history.rs | 23 ++- src/modules/usdt/paymaster.rs | 31 ++-- src/modules/usdt/rpc.rs | 21 ++- src/modules/usdt/store.rs | 40 +++++- src/modules/usdt/tests.rs | 263 +++++++++++++++++++++++++++++++++- src/modules/usdt/wallet.rs | 61 ++++++-- tests/usdt-fork/provider.mjs | 1 + 10 files changed, 411 insertions(+), 41 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c182e83..743a64e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,7 +20,7 @@ Android bindings are built and published by `.github/workflows/gradle-publish.ym ```bash cargo test # All tests -cargo test modules:: # Single module (scanner, lnurl, onchain, activity, blocktank, boltz, trezor, jade, hardware_wallet, ur, pubky) +cargo test modules:: # Single module (scanner, lnurl, onchain, activity, blocktank, boltz, trezor, jade, hardware_wallet, ur, pubky, usdt) ``` ## Lint & Format @@ -35,7 +35,7 @@ Android bindings use ktlint via Gradle plugin (`org.jlleitschuh.gradle.ktlint`), ## Architecture - `src/lib.rs` — UniFFI exports and module re-exports -- `src/modules/`: core modules: scanner, lnurl, onchain, activity, blocktank, boltz, trezor, jade, hardware_wallet, ur, pubky +- `src/modules/`: core modules: scanner, lnurl, onchain, activity, blocktank, boltz, trezor, jade, hardware_wallet, ur, pubky, usdt - `bindings/` — Platform-specific binding outputs (ios/, android/, python/) - `build.sh`, `build_ios.sh`, `build_android.sh`, `build_python.sh` — Build scripts diff --git a/Package.swift b/Package.swift index 2ea8a85..74c8b9f 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "eed89e4a6d060e064bc91f8a66115ffff90fff6edd659c42024077c49a8982b2" +let checksum = "e491751672800cee2c82587e47aad05e3c18d39e9ac5cc2bc4c3b7aafeaf4c40" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index 3f6f9ab..9a5935d 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -14,7 +14,7 @@ Owned mnemonic/passphrase/seed buffers are zeroized and signing keys are erased ## Quotes and fees -`quote_transfer` takes a raw recipient, positive atomic amount; it never receives signing credentials. Local quotes last at most 120 seconds and newly quoted paymaster terms must expire within 15 minutes. `send` validates the owner, nonces, balance, gas estimates, current gas prices and deadlines before signing the stored plan. Changed terms require a new review; signing cannot raise the approved fee. +`quote_transfer` takes a raw recipient and a positive atomic amount; it never receives signing credentials. Local quotes last at most 120 seconds and newly quoted paymaster terms must expire within 15 minutes. `send` validates the owner, nonces, balance, gas estimates, the current slow gas-price recommendation and deadlines before signing the stored plan. Quotes use the fast gas-price recommendation; a modest price increase does not invalidate a quote that still covers the current slow recommendation. Changes beyond the approved bounds require a new review; signing cannot raise the approved fee. The pinned ERC-20 paymaster collects USDT. Its finite approval includes a 5% margin; the displayed maximum fee comes from signed gas limits and paymaster terms, not the allowance. Call/pre-verification estimates receive 10% execution/L1-data headroom; the charged pre-verification margin is included in the maximum. A residual paymaster allowance can remain and is reset to a finite amount on the next payment. @@ -28,7 +28,7 @@ A matching operation event settles the payment. Expired signed paymaster terms a Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls recover payment/fee attribution; unknown wrappers preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. -`sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. +`sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Completed fallback scans are retained by canonical block hash within the revisit window. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. Scans trail the reported tip by two blocks and revisit 4096 blocks for delayed indexing. This is not reorg rollback: previously recorded orphaned activity is not retracted. Providers must supply complete filtered logs, canonical blocks/receipts and historical state. @@ -44,7 +44,7 @@ Both chain and bundler endpoints must be controlled, credential-free HTTPS URLs; Run `cargo test --locked --lib modules::usdt`; CI runs these deterministic tests. They cover independent signing/address vectors, fee bounds, uncertain submission, nonce recovery and restored history. Fixtures use public test credentials. -For the ignored deployed-contract test, start a fresh Arbitrum Anvil fork on port 18545 and `tests/usdt-fork/provider.mjs` on 18546 after installing its pinned dependencies. Run `cargo test deployed_contracts_collect_usdt_fees_without_account_eth -- --ignored`. The fixture requires Anvil, sets local balances/signing terms and checks deployed bytecode; it does not establish real provider pricing. +For the ignored deployed-contract test, start a fresh Arbitrum Anvil fork on port 18545 and `tests/usdt-fork/provider.mjs` on 18546 after installing its pinned dependencies. Run `cargo test deployed_contracts_collect_usdt_fees_without_account_eth -- --ignored`. The fixture requires Anvil, sets local balances/signing terms and executes deployed contracts; it does not establish real provider pricing. To include the service, start it with `NODE_ENV=test ARBITRUM_RPC_URL=http://127.0.0.1:18545 LOCAL_PROVIDER_URL=http://127.0.0.1:18546`, then pass `USDT_FORK_RPC_URL=http://127.0.0.1:3100/v1/usdt/chain-rpc` and `USDT_FORK_BUNDLER_URL=http://127.0.0.1:3100/v1/usdt/rpc` to the ignored test. diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index b0c2037..1add3cd 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -25,6 +25,7 @@ impl UsdtWallet { if start > tip { return Err(UsdtError::NetworkUnavailable); } + let mut ceiling = MAX_LOG_RANGE; let mut next = start; let mut width = self .history_range_limit @@ -45,7 +46,7 @@ impl UsdtWallet { Err(_) => { self.history_range_limit .store((width / 2).max(1), Ordering::Relaxed); - return Ok(false); + return Err(UsdtError::NetworkUnavailable); } }; match result { @@ -92,6 +93,7 @@ impl UsdtWallet { } Err(UsdtError::LogRangeTooLarge) if next < end => { width = (width / 2).max(1); + ceiling = width; self.history_range_limit.store(width, Ordering::Relaxed); continue; } @@ -113,7 +115,7 @@ impl UsdtWallet { } next = end + 1; self.store.save_history_progress(next)?; - width = (width * 2).min(MAX_LOG_RANGE); + width = (width * 2).min(ceiling); self.history_range_limit.store(width, Ordering::Relaxed); width = width.min(tip - next + 1); } @@ -126,6 +128,10 @@ impl UsdtWallet { deadline: tokio::time::Instant, ) -> Result { let block = self.rpc.block(number).await?; + let block_hash = format!("{:#x}", block.hash); + if self.store.begin_history_block(number, &block_hash)? { + return Ok(true); + } let timestamp = u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; if self.store.history_progress()? != Some(number) { self.store.save_history_progress(number)?; @@ -141,6 +147,10 @@ impl UsdtWallet { let receipt = self.rpc.block_receipt(*hash, &block, number).await?; self.save_receipt_history(&id, timestamp, &receipt).await?; } + if self.rpc.block(number).await?.hash != block.hash { + return Err(UsdtError::NetworkUnavailable); + } + self.store.complete_history_block(number, &block_hash)?; Ok(true) } @@ -263,6 +273,10 @@ impl UsdtWallet { None }; for (event, saved) in owned_operations { + // Unknown fee collection keeps its raw debits and refunds intact. + if event.paymaster != super::paymaster::PAYMASTER { + continue; + } let operation_hash = format!("{:#x}", event.userOpHash); let (recipient, amount) = if let Some(saved) = saved { (saved.recipient, saved.amount) @@ -320,7 +334,10 @@ fn decode_payment(data: &[u8]) -> Result, UsdtError> { if target == TOKEN { if let Ok(call) = Erc20::transferCall::abi_decode(&data) { payment_count += 1; - payment = Some((call.recipient, token_amount(call.amount)?)); + let Ok(amount) = token_amount(call.amount) else { + return Ok(None); + }; + payment = Some((call.recipient, amount)); } else if Erc20::approveCall::abi_decode(&data).is_err() { supported = false; } diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs index 72fafdb..cef65e7 100644 --- a/src/modules/usdt/paymaster.rs +++ b/src/modules/usdt/paymaster.rs @@ -20,6 +20,12 @@ struct GasPrice { max_priority_fee_per_gas: U256, } +#[derive(Deserialize)] +struct GasPrices { + slow: GasPrice, + fast: GasPrice, +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct TokenQuote { @@ -118,7 +124,7 @@ impl Pimlico { && !quote.exchange_rate.is_zero() }) .ok_or(UsdtError::UnsupportedRoute)?; - let price = self.gas_price().await?; + let price = self.gas_prices().await?.fast; let dummy_signature = Bytes::from_static(&alloy_primitives::hex!("fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c")); let mut op = UserOperation { sender: address, @@ -209,7 +215,7 @@ impl Pimlico { } pub async fn validate_gas(&self, op: &UserOperation) -> Result<(), UsdtError> { - let price = self.gas_price().await?; + let price = self.gas_prices().await?.slow; if price.max_fee_per_gas > op.max_fee_per_gas || price.max_priority_fee_per_gas > op.max_priority_fee_per_gas { @@ -234,21 +240,24 @@ impl Pimlico { Ok(()) } - async fn gas_price(&self) -> Result { - #[derive(Deserialize)] - struct Prices { - fast: GasPrice, - } - let price: Prices = self + async fn gas_prices(&self) -> Result { + let prices: GasPrices = self .rpc .call("pimlico_getUserOperationGasPrice", json!([])) .await?; - if price.fast.max_fee_per_gas.is_zero() - || price.fast.max_priority_fee_per_gas > price.fast.max_fee_per_gas + for price in [&prices.slow, &prices.fast] { + if price.max_fee_per_gas.is_zero() + || price.max_priority_fee_per_gas > price.max_fee_per_gas + { + return Err(UsdtError::InvalidResponse); + } + } + if prices.slow.max_fee_per_gas > prices.fast.max_fee_per_gas + || prices.slow.max_priority_fee_per_gas > prices.fast.max_priority_fee_per_gas { return Err(UsdtError::InvalidResponse); } - Ok(price.fast) + Ok(prices) } } diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs index c24fb09..e667030 100644 --- a/src/modules/usdt/rpc.rs +++ b/src/modules/usdt/rpc.rs @@ -87,6 +87,9 @@ impl Rpc { } else { 2_097_152 }; + if status.is_server_error() { + return Err(UsdtError::NetworkUnavailable); + } let body = bounded_json(response, limit, overflow).await; let response: Response = match body.and_then(|value| serde_json::from_value(value).map_err(Into::into)) { @@ -116,8 +119,19 @@ impl Rpc { if error.code == -32002 { return Err(UsdtError::NetworkUnavailable); } - if message.contains("insufficient funds") || message.contains("insufficient balance") { - return Err(UsdtError::InsufficientBalance); + if matches!( + method, + "eth_chainId" + | "eth_blockNumber" + | "eth_getCode" + | "eth_getTransactionCount" + | "eth_call" + | "eth_getLogs" + | "eth_getBlockByNumber" + | "eth_getTransactionReceipt" + | "eth_getTransactionByHash" + ) { + return Err(UsdtError::NetworkUnavailable); } return Err(UsdtError::TransactionRejected { reason: error.message.chars().take(200).collect(), @@ -151,6 +165,9 @@ impl Rpc { let receipt: Value = self .call("eth_getTransactionReceipt", json!([hash])) .await?; + if receipt.is_null() { + return Err(UsdtError::NetworkUnavailable); + } if serde_json::from_value::(receipt["transactionHash"].clone())? != hash || serde_json::from_value::(receipt["blockHash"].clone())? != block.hash || serde_json::from_value::(receipt["blockNumber"].clone())? != U256::from(number) diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs index 6c613eb..3ee9951 100644 --- a/src/modules/usdt/store.rs +++ b/src/modules/usdt/store.rs @@ -28,6 +28,7 @@ impl Store { CREATE TABLE IF NOT EXISTS usdt_sync (id INTEGER PRIMARY KEY CHECK(id=1), newest INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_history_progress (id INTEGER PRIMARY KEY CHECK(id=1), next INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_history_receipts (hash TEXT PRIMARY KEY); + CREATE TABLE IF NOT EXISTS usdt_history_blocks (number INTEGER PRIMARY KEY, hash TEXT NOT NULL, complete INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_quotes (id TEXT PRIMARY KEY, data TEXT NOT NULL); CREATE TABLE IF NOT EXISTS usdt_nonce_recovery (id TEXT PRIMARY KEY, block_hash TEXT NOT NULL, next_transaction INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_transfers (id TEXT PRIMARY KEY, hash TEXT NOT NULL, raw TEXT, data TEXT NOT NULL);")?; @@ -189,6 +190,10 @@ impl Store { let tx = connection.transaction()?; tx.execute("DELETE FROM usdt_history_progress", [])?; tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.execute( + "DELETE FROM usdt_history_blocks WHERE number < ?1", + [newest.saturating_sub(4096)], + )?; tx.execute("INSERT INTO usdt_sync (id,newest) VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET newest=excluded.newest", [newest])?; tx.commit()?; Ok(()) @@ -208,6 +213,10 @@ impl Store { pub fn save_history_progress(&self, next: u64) -> Result<(), UsdtError> { let mut connection = self.connection()?; let tx = connection.transaction()?; + tx.execute( + "DELETE FROM usdt_history_blocks WHERE number < ?1", + [next.saturating_sub(4096)], + )?; tx.execute("INSERT INTO usdt_history_progress VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET next=excluded.next", [next])?; tx.execute("DELETE FROM usdt_history_receipts", [])?; tx.commit()?; @@ -216,11 +225,40 @@ impl Store { pub fn transaction_timestamp(&self, hash: &str) -> Result, UsdtError> { Ok(self.connection()?.query_row( - "SELECT json_extract(data, '$.timestamp') FROM usdt_transfers WHERE json_extract(data, '$.tx_hash')=?1 LIMIT 1", + "SELECT json_extract(data, '$.timestamp') FROM usdt_transfers WHERE json_extract(data, '$.tx_hash')=?1 AND json_extract(data, '$.status') != 'Pending' LIMIT 1", [hash], |row| row.get(0), ).optional()?) } + pub fn begin_history_block(&self, number: u64, hash: &str) -> Result { + let mut connection = self.connection()?; + let tx = connection.transaction()?; + let saved: Option<(String, bool)> = tx + .query_row( + "SELECT hash, complete FROM usdt_history_blocks WHERE number=?1", + [number], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional()?; + if let Some((saved_hash, complete)) = saved { + if saved_hash == hash { + return Ok(complete); + } + tx.execute("DELETE FROM usdt_history_receipts", [])?; + } + tx.execute("INSERT INTO usdt_history_blocks VALUES (?1,?2,0) ON CONFLICT(number) DO UPDATE SET hash=excluded.hash,complete=0", params![number, hash])?; + tx.commit()?; + Ok(false) + } + + pub fn complete_history_block(&self, number: u64, hash: &str) -> Result<(), UsdtError> { + self.connection()?.execute( + "UPDATE usdt_history_blocks SET complete=1 WHERE number=?1 AND hash=?2", + params![number, hash], + )?; + Ok(()) + } + pub fn has_history_receipt(&self, hash: &str) -> Result { Ok(self.connection()?.query_row( "SELECT EXISTS(SELECT 1 FROM usdt_history_receipts WHERE hash=?1)", diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index 0b2984f..cd307fc 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -92,6 +92,11 @@ fn wallet_requires_both_provider_endpoints() { for (rpc, bundler) in [ ("", "https://provider.example"), ("https://provider.example", ""), + ("not a url", "https://provider.example"), + ( + "https://provider.example", + "https://example.com?api-key=secret", + ), ] { assert!(matches!( UsdtWallet::new( @@ -249,6 +254,9 @@ struct ChainState { incoming_count: u64, block_reads: usize, fail_block_read_at: Option, + authorization_change_on_block_read: bool, + receipt_reads: usize, + receipt_response: Option, } impl Drop for MockChain { fn drop(&mut self) { @@ -297,6 +305,9 @@ impl MockChain { incoming_count: 0, block_reads: 0, fail_block_read_at: None, + authorization_change_on_block_read: false, + receipt_reads: 0, + receipt_response: None, })); let server_state = state.clone(); let task = tokio::spawn(async move { @@ -334,6 +345,33 @@ impl MockChain { } let body: serde_json::Value = serde_json::from_slice(&request[header_end..]).unwrap(); + let path = String::from_utf8_lossy(&request[..header_end]) + .lines() + .next() + .unwrap() + .split_whitespace() + .nth(1) + .unwrap() + .to_owned(); + let method = body["method"].as_str().unwrap(); + let bundler = matches!( + method, + "pimlico_getTokenQuotes" + | "pimlico_getUserOperationGasPrice" + | "pm_getPaymasterData" + | "pm_getPaymasterStubData" + | "eth_estimateUserOperationGas" + | "eth_sendUserOperation" + ); + if path == "/chain" { + assert!(!bundler, "Bundler method on chain endpoint"); + } + if path == "/bundler" { + assert!( + bundler || method == "eth_chainId", + "Chain method on bundler endpoint" + ); + } let delay = body["method"] == "eth_estimateUserOperationGas" && std::mem::take(&mut server_state.lock().unwrap().delay_gas_estimate); if delay { @@ -350,8 +388,8 @@ impl MockChain { UsdtWallet::new( usdt_address(TEST_PHRASE.into(), None).unwrap(), dir.path().join("usdt.sqlite").to_string_lossy().into(), - self.url.clone(), - self.url.clone(), + format!("{}/chain", self.url), + format!("{}/bundler", self.url), ) .unwrap() } @@ -381,6 +419,9 @@ impl ChainState { "eth_blockNumber" => json!(U256::from(self.tip)), "eth_getBlockByNumber" => { self.block_reads += 1; + if std::mem::take(&mut self.authorization_change_on_block_read) { + self.authorization_nonce += 1; + } if self.fail_block_read_at == Some(self.block_reads) { self.fail_block_read_at = None; return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"temporarily unavailable"}}); @@ -414,7 +455,7 @@ impl ChainState { json!({"quotes":[{"token":types::TOKEN,"paymaster":self.paymaster,"postOpGas":"0xc350","exchangeRate":U256::from(3_000_000_000u64)}]}) } "pimlico_getUserOperationGasPrice" => { - json!({"fast":{"maxFeePerGas":U256::from(self.gas_price),"maxPriorityFeePerGas":U256::from(100000)}}) + json!({"slow":{"maxFeePerGas":U256::from(self.gas_price * 9 / 10),"maxPriorityFeePerGas":U256::from(100000)},"fast":{"maxFeePerGas":U256::from(self.gas_price),"maxPriorityFeePerGas":U256::from(100000)}}) } "pm_getPaymasterData" | "pm_getPaymasterStubData" => { let mut data = vec![0; 118]; @@ -547,6 +588,10 @@ impl ChainState { } } "eth_getTransactionReceipt" => { + self.receipt_reads += 1; + if let Some(response) = &self.receipt_response { + return json!({"jsonrpc":"2.0","id":1,"result":response}); + } if self.hide_receipts || self .receipt_failure @@ -700,7 +745,12 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { .iter() .all(|item| serde_json::to_value(item).unwrap() == serde_json::to_value(&op).unwrap())); chain.state.lock().unwrap().mined = true; + chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(60); let history = wallet.refresh_transfers().await.unwrap(); + assert_eq!( + history[0].timestamp, + u64::try_from(chain.state.lock().unwrap().timestamp).unwrap() + ); assert_eq!(history.len(), 1); assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); assert_eq!(history[0].fee, Some(123)); @@ -930,6 +980,10 @@ async fn bundled_operations_cannot_contribute_another_payments_fee() { log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:own_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(123), exchangeRate:U256::from(1) }.encode_log_data()), log(account::ENTRY_POINT, event(own_hash)), ]}); + assert_eq!( + transaction::operation_logs(&receipt, own_hash).unwrap(), + &receipt["logs"].as_array().unwrap()[2..] + ); wallet.settle(&mut transfer, &receipt, false).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Failed); assert_eq!(transfer.fee, Some(123)); @@ -1004,6 +1058,15 @@ async fn history_preserves_receipts_with_external_account_call_shapes() { .await .unwrap(); let original = chain.state.lock().unwrap().operations[0].call_data.clone(); + let oversized = account::batch(&[( + types::TOKEN, + transaction::Erc20::transferCall { + recipient: RECIPIENT.parse().unwrap(), + amount: U256::MAX, + } + .abi_encode() + .into(), + )]); let single = account::SimpleAccount::executeCall { target: types::TOKEN, value: U256::ZERO, @@ -1019,6 +1082,7 @@ async fn history_preserves_receipts_with_external_account_call_shapes() { for (call_data, outer, expected_outgoing) in [ (original.clone(), None, true), (single, None, true), + (oversized, None, false), (Bytes::from_static(&[1, 2, 3, 4]), None, false), (original, Some(Bytes::from_static(&[5, 6, 7, 8])), false), ] { @@ -1261,7 +1325,7 @@ async fn interrupted_history_resumes_without_repeating_completed_work() { }) .await .unwrap(); - assert!(matches!(error, UsdtError::TransactionRejected { .. })); + assert!(matches!(error, UsdtError::NetworkUnavailable)); assert_eq!(restored.history().unwrap().len(), 24); assert!(restored.store.history_progress().unwrap().is_some()); drop(restored); @@ -1348,7 +1412,7 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { } let error = wallet.sync_history().await.unwrap_err(); if code == -32000 { - assert!(matches!(error, UsdtError::TransactionRejected { .. })); + assert!(matches!(error, UsdtError::NetworkUnavailable)); } else { assert!(matches!(error, UsdtError::RateLimited)); } @@ -1798,6 +1862,11 @@ async fn dense_block_history_recovers_large_receipts_without_skipping_after_rest assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); assert_eq!(history[0].fee, Some(123)); assert_eq!(restored.store.synced_block().unwrap(), Some(20001)); + let reads = chain.state.lock().unwrap().receipt_reads; + drop(restored); + let restored = chain.wallet(&dir); + sync_history_to_tip(&restored).await; + assert_eq!(chain.state.lock().unwrap().receipt_reads, reads); } #[tokio::test] @@ -1825,3 +1894,187 @@ async fn zero_value_transfers_do_not_require_receipts_or_timestamps() { assert!(wallet.sync_history().await.unwrap()); assert!(wallet.history().unwrap().is_empty()); } + +#[tokio::test] +async fn first_submission_precheck_releases_an_operation_that_was_never_sent() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + chain + .state + .lock() + .unwrap() + .authorization_change_on_block_read = true; + assert!(matches!( + wallet + .send(quote.id.clone(), TEST_PHRASE.into(), None) + .await, + Err(UsdtError::QuoteExpired) + )); + assert!(chain.state.lock().unwrap().operations.is_empty()); + assert!(wallet.store.pending_plan("e.id).unwrap().is_none()); + let failed = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + assert_eq!(failed.status, UsdtTransferStatus::Failed); + assert_eq!(failed.received_amount, 0); + assert_eq!(failed.fee, Some(0)); + wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); +} + +#[tokio::test] +async fn moderate_gas_price_movement_preserves_the_approved_fee() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let plan = wallet.store.quote("e.id).unwrap().plan; + chain.state.lock().unwrap().gas_price = 52_000_000; + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + assert_eq!( + chain.state.lock().unwrap().operations[0].max_fee_per_gas, + plan.operation.max_fee_per_gas + ); +} + +#[tokio::test] +async fn unknown_paymaster_history_preserves_principal_fee_and_refund() { + use alloy_primitives::{Address, B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let payer = Address::repeat_byte(0xab); + let movement = |from, to, value, index| { + let event = transaction::Erc20::Transfer { + from, + to, + value: U256::from(value), + } + .encode_log_data(); + json!({"address": types::TOKEN, "topics":event.topics(), "data":event.data, "logIndex": U256::from(index)}) + }; + { + let mut state = chain.state.lock().unwrap(); + let op = &state.operations[0]; + let event = transaction::EntryPoint::UserOperationEvent { + userOpHash: op.hash(types::CHAIN_ID).unwrap(), + sender: wallet.address, + paymaster: payer, + nonce: U256::ZERO, + success: true, + actualGasCost: U256::from(1), + actualGasUsed: U256::from(1), + } + .encode_log_data(); + let log = json!({"address":account::ENTRY_POINT,"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x3"}); + state.receipt_logs = Some(vec![ + movement(wallet.address, payer, 200u64, 0u64), + movement( + wallet.address, + RECIPIENT.parse().unwrap(), + 1_000_000u64, + 1u64, + ), + movement(payer, wallet.address, 50u64, 2u64), + log.clone(), + ]); + state.log_response = Some(vec![log]); + state.tip += 3; + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 3); + assert_eq!( + history + .iter() + .filter(|t| !t.is_incoming) + .map(|t| t.amount) + .sum::(), + 1_000_200 + ); + assert_eq!( + history + .iter() + .filter(|t| t.is_incoming) + .map(|t| t.amount) + .sum::(), + 50 + ); +} + +#[tokio::test] +async fn settlement_requires_matching_canonical_receipts() { + use alloy_primitives::B256; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let receipt = { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + state + .respond(&json!({"method":"eth_getTransactionReceipt","params":[B256::repeat_byte(7)]})) + ["result"] + .clone() + }; + for (field, value) in [ + ("transactionHash", json!(B256::ZERO)), + ("blockHash", json!(B256::ZERO)), + ("blockNumber", json!("0x1")), + ] { + let mut invalid = receipt.clone(); + invalid[field] = value; + chain.state.lock().unwrap().receipt_response = Some(invalid); + assert!(matches!( + wallet.refresh_transfers().await, + Err(UsdtError::InvalidResponse) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + } + chain.state.lock().unwrap().receipt_response = Some(serde_json::Value::Null); + assert!(matches!( + wallet.refresh_transfers().await, + Err(UsdtError::NetworkUnavailable) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + chain.state.lock().unwrap().receipt_response = None; + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Confirmed + ); +} diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 1ff5049..3f3a38b 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -39,11 +39,11 @@ impl UsdtWallet { return Err(UsdtError::NotConfigured); } let address = parse_address(&address)?; - let store = Store::open(&storage_path, &format!("{CHAIN_ID}:{}", address))?; let rpc = Rpc::new(rpc_url, CHAIN_ID)?; let paymaster = Pimlico { rpc: rpc.with_url(bundler_url)?, }; + let store = Store::open(&storage_path, &format!("{CHAIN_ID}:{}", address))?; Ok(Arc::new(Self { address, rpc, @@ -80,7 +80,15 @@ impl UsdtWallet { return Err(UsdtError::InvalidAmount); } let recipient = parse_address(recipient.trim())?; - if recipient == self.address || recipient == TOKEN { + if [ + self.address, + TOKEN, + ENTRY_POINT, + PAYMASTER, + super::account::DELEGATE, + ] + .contains(&recipient) + { return Err(UsdtError::InvalidAddress); } self.store.require_no_pending()?; @@ -173,7 +181,7 @@ impl UsdtWallet { } let (hash, raw) = data.plan.sign(&key)?; drop(key); - let transfer = UsdtTransfer { + let mut transfer = UsdtTransfer { id: quote_id, tx_hash: String::new(), user_operation_hash: Some(format!("{hash:#x}")), @@ -188,7 +196,19 @@ impl UsdtWallet { }; self.store.record_signed(&transfer, &raw)?; // After persistence a lost response is indeterminate. Retry only the identical signed operation. - let _ = self.broadcast(&data.plan, hash).await; + if let Err(error) = self.broadcast(&data.plan, hash).await { + // These errors occur before submission; later retries may already be queued. + if matches!( + error, + UsdtError::QuoteExpired | UsdtError::UnsupportedDelegation + ) { + transfer.status = UsdtTransferStatus::Failed; + transfer.received_amount = 0; + transfer.fee = Some(0); + self.store.update_transfer(&transfer)?; + return Err(error); + } + } Ok(transfer) } @@ -352,6 +372,8 @@ impl UsdtWallet { transfer.received_amount = 0; transfer.fee = Some(0); } + transfer.timestamp = + u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; return self.store.update_transfer(transfer); } self.store @@ -419,18 +441,25 @@ impl UsdtWallet { if self.authorization().await?.nonce != plan.operation.eip7702_auth.nonce { return Err(UsdtError::QuoteExpired); } - let hash: B256 = self + let result: Result = self .paymaster .rpc .call( "eth_sendUserOperation", json!([plan.operation, ENTRY_POINT]), ) - .await?; - if hash != expected_hash { - return Err(UsdtError::InvalidResponse); + .await; + match result { + Ok(hash) if hash == expected_hash => Ok(()), + Ok(_) => { + log::warn!("USDT submission returned an unexpected operation hash; retaining pending payment"); + Err(UsdtError::InvalidResponse) + } + Err(error) => { + log::warn!("USDT submission could not be confirmed; retaining pending payment"); + Err(error) + } } - Ok(()) } async fn settle_from_log( &self, @@ -443,11 +472,17 @@ impl UsdtWallet { } transfer.tx_hash = serde_json::from_value(log["transactionHash"].clone())?; transfer.explorer_url = format!("{EXPLORER}/tx/{}", transfer.tx_hash); - let receipt = self - .rpc - .call("eth_getTransactionReceipt", json!([transfer.tx_hash])) - .await?; + let number = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) + .map_err(|_| UsdtError::InvalidResponse)?; + let block = self.rpc.block(number).await?; + let hash = transfer + .tx_hash + .parse() + .map_err(|_| UsdtError::InvalidResponse)?; + let receipt = self.rpc.block_receipt(hash, &block, number).await?; self.settle(transfer, &receipt, event.success)?; + transfer.timestamp = + u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; self.store.update_transfer(transfer) } diff --git a/tests/usdt-fork/provider.mjs b/tests/usdt-fork/provider.mjs index e4650a3..68b730b 100644 --- a/tests/usdt-fork/provider.mjs +++ b/tests/usdt-fork/provider.mjs @@ -138,6 +138,7 @@ async function dispatch(method, params) { }; if (method === 'pimlico_getUserOperationGasPrice') return { + slow: { maxFeePerGas: toBeHex(90_000_000), maxPriorityFeePerGas: toBeHex(1_000_000) }, fast: { maxFeePerGas: toBeHex(100_000_000), maxPriorityFeePerGas: toBeHex(1_000_000) }, }; if (method === 'eth_estimateUserOperationGas') return gas; From 08122040cd7f3bc04ab9bb644d90466089dde09a Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 25 Sep 2026 09:07:44 +0300 Subject: [PATCH 6/9] fix: verify usdt settlement and recovery evidence --- Package.swift | 2 +- bindings/ios/bitkitcore.swift | 18 +- src/modules/usdt/README.md | 6 +- src/modules/usdt/errors.rs | 2 +- src/modules/usdt/history.rs | 118 +++++++----- src/modules/usdt/paymaster.rs | 22 ++- src/modules/usdt/payment_request.rs | 15 +- src/modules/usdt/rpc.rs | 29 ++- src/modules/usdt/store.rs | 7 - src/modules/usdt/tests.rs | 289 +++++++++++++++++++++++----- src/modules/usdt/types.rs | 2 + src/modules/usdt/wallet.rs | 49 ++--- tests/usdt-fork/provider.mjs | 2 +- 13 files changed, 414 insertions(+), 147 deletions(-) diff --git a/Package.swift b/Package.swift index 74c8b9f..9cb894f 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "e491751672800cee2c82587e47aad05e3c18d39e9ac5cc2bc4c3b7aafeaf4c40" +let checksum = "7052ccab97f8cb6e85b77d05fd23355970f4d9b81d7b7f37707d2ea0e976781b" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index 4c409a7..2677fb8 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -15906,12 +15906,20 @@ public func FfiConverterTypeUrDecoderStatus_lower(_ value: UrDecoderStatus) -> R public struct UsdtPaymentRequest { public var recipient: String public var amount: UInt64? + /** + * An explicit network in the payment URI; bare addresses have no restriction. + */ + public var chainId: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(recipient: String, amount: UInt64?) { + public init(recipient: String, amount: UInt64?, + /** + * An explicit network in the payment URI; bare addresses have no restriction. + */chainId: UInt64?) { self.recipient = recipient self.amount = amount + self.chainId = chainId } } @@ -15928,12 +15936,16 @@ extension UsdtPaymentRequest: Equatable, Hashable { if lhs.amount != rhs.amount { return false } + if lhs.chainId != rhs.chainId { + return false + } return true } public func hash(into hasher: inout Hasher) { hasher.combine(recipient) hasher.combine(amount) + hasher.combine(chainId) } } @@ -15949,13 +15961,15 @@ public struct FfiConverterTypeUsdtPaymentRequest: FfiConverterRustBuffer { return try UsdtPaymentRequest( recipient: FfiConverterString.read(from: &buf), - amount: FfiConverterOptionUInt64.read(from: &buf) + amount: FfiConverterOptionUInt64.read(from: &buf), + chainId: FfiConverterOptionUInt64.read(from: &buf) ) } public static func write(_ value: UsdtPaymentRequest, into buf: inout [UInt8]) { FfiConverterString.write(value.recipient, into: &buf) FfiConverterOptionUInt64.write(value.amount, into: &buf) + FfiConverterOptionUInt64.write(value.chainId, into: &buf) } } diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index 9a5935d..7f7bef6 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -18,15 +18,15 @@ Owned mnemonic/passphrase/seed buffers are zeroized and signing keys are erased The pinned ERC-20 paymaster collects USDT. Its finite approval includes a 5% margin; the displayed maximum fee comes from signed gas limits and paymaster terms, not the allowance. Call/pre-verification estimates receive 10% execution/L1-data headroom; the charged pre-verification margin is included in the maximum. A residual paymaster allowance can remain and is reset to a finite amount on the next payment. -`usdt_parse_payment_request` accepts raw addresses and chain-qualified ERC-681 requests for the pinned token, with exact atomic/scientific amounts. Ambiguous or unsupported parameters are rejected. The caller reviews the parsed amount before requesting a quote. +`usdt_parse_payment_request` accepts raw addresses and chain-qualified ERC-681 requests for the pinned token, with exact atomic/scientific amounts. Ambiguous or unsupported parameters are rejected. The returned `chain_id` preserves explicit network restrictions; bare addresses leave it unset. Callers must honor it and review the parsed amount before requesting a quote. ## Persistence and recovery Signed operations persist atomically before submission. Lost or rejected submission responses do not prove nonexecution: recovery retries only the identical signed operation. A quote ID cannot authorize a second payment. One source-chain payment remains pending at a time. -A matching operation event settles the payment. Expired signed paymaster terms and an unchanged confirmed EntryPoint nonce release an unmined operation; the shorter quote deadline does not. With an advanced nonce and missing indexed events, recovery checks every receipt in the consuming block. A matching event settles/replaces the payment; complete absence proves external nonce consumption. Missing receipts preserve the pending operation. Progress is stored by payment and block hash so interruption does not restart the proof or carry it onto another block. +A matching event in a canonical receipt settles the payment. Discovery logs alone never decide the outcome. Expired signed paymaster terms and a confirmed EntryPoint nonce that has not passed the signed nonce release an unmined operation; the shorter quote deadline does not. With an advanced nonce and missing indexed events, recovery checks every receipt in the consuming block. A matching event settles/replaces the payment; complete absence proves external nonce consumption. Missing receipts preserve the pending operation. Progress is stored by payment and block hash so interruption does not restart the proof or carry it onto another block. -Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls recover payment/fee attribution; unknown wrappers preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. +Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls and paymaster modes recover payment/fee attribution; unknown wrappers or payment modes preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. `sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Completed fallback scans are retained by canonical block hash within the revisit window. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. diff --git a/src/modules/usdt/errors.rs b/src/modules/usdt/errors.rs index 059f2c2..688d7b8 100644 --- a/src/modules/usdt/errors.rs +++ b/src/modules/usdt/errors.rs @@ -18,7 +18,7 @@ pub enum UsdtError { QuoteExpired, #[error("A USDT transaction is pending. Wait for confirmation before sending again")] PendingTransfer, - #[error("The selected USDT0 route is unavailable")] + #[error("The selected USDT payment route is unavailable")] UnsupportedRoute, #[error("USDT payments are not configured for this app build")] NotConfigured, diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index 1add3cd..d641435 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -5,7 +5,7 @@ use super::{ types::{EXPLORER, TOKEN}, UsdtError, UsdtTransfer, UsdtTransferStatus, UsdtWallet, }; -use alloy_primitives::{Address, Bytes, U256}; +use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_sol_types::{SolCall, SolEvent}; use serde_json::{json, Value}; use std::{collections::BTreeMap, sync::atomic::Ordering}; @@ -25,14 +25,14 @@ impl UsdtWallet { if start > tip { return Err(UsdtError::NetworkUnavailable); } - let mut ceiling = MAX_LOG_RANGE; + let initial_limit = self.history_range_limit.load(Ordering::Relaxed); + let mut ceiling = initial_limit; let mut next = start; - let mut width = self - .history_range_limit - .load(Ordering::Relaxed) - .min(tip - start + 1); + let mut width = initial_limit.min(tip - start + 1); while next <= tip { if tokio::time::Instant::now() >= deadline { + self.history_range_limit + .store((ceiling * 2).min(MAX_LOG_RANGE), Ordering::Relaxed); return Ok(false); } let end = next.saturating_add(width - 1).min(tip); @@ -46,7 +46,11 @@ impl UsdtWallet { Err(_) => { self.history_range_limit .store((width / 2).max(1), Ordering::Relaxed); - return Err(UsdtError::NetworkUnavailable); + return if next > start { + Ok(false) + } else { + Err(UsdtError::NetworkUnavailable) + }; } }; match result { @@ -54,7 +58,7 @@ impl UsdtWallet { if self.store.history_progress()? != Some(next) { self.store.save_history_progress(next)?; } - let mut timestamps = BTreeMap::new(); + let mut blocks = BTreeMap::new(); for ((block, hash), logs) in transactions { if self.store.has_history_receipt(&hash)? { continue; @@ -70,23 +74,32 @@ impl UsdtWallet { .and_then(|data| Erc20::Transfer::decode_log_data(&data).ok()) .is_some_and(|event| event.from == self.address) }); + let canonical = match blocks.entry(block) { + std::collections::btree_map::Entry::Occupied(entry) => entry.into_mut(), + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(self.rpc.block(block).await?) + } + }; + for log in &logs { + if serde_json::from_value::(log["blockHash"].clone())? + != canonical.hash + { + return Err(UsdtError::NetworkUnavailable); + } + } let receipt = if needs_receipt { self.rpc - .call("eth_getTransactionReceipt", json!([hash])) + .block_receipt( + hash.parse().map_err(|_| UsdtError::InvalidResponse)?, + canonical.hash, + block, + ) .await? } else { json!({"logs": logs}) }; - let timestamp = - if let Some(timestamp) = self.store.transaction_timestamp(&hash)? { - timestamp - } else if let Some(timestamp) = timestamps.get(&block) { - *timestamp - } else { - let timestamp = self.block_timestamp(block).await?; - timestamps.insert(block, timestamp); - timestamp - }; + let timestamp = u64::try_from(canonical.timestamp) + .map_err(|_| UsdtError::InvalidResponse)?; self.save_receipt_history(&hash, timestamp, &receipt) .await?; } @@ -101,16 +114,26 @@ impl UsdtWallet { if !self.scan_block(next, deadline).await? { return Ok(false); } + // A dense block is not a range limit for the blocks after it. + ceiling = MAX_LOG_RANGE; + width = MAX_LOG_RANGE; } Err(UsdtError::NetworkUnavailable) => { self.history_range_limit .store((width / 2).max(1), Ordering::Relaxed); - return Err(UsdtError::NetworkUnavailable); + return if next > start { + Ok(false) + } else { + Err(UsdtError::NetworkUnavailable) + }; } Err(error) => return Err(error), } if end == tip { self.store.complete_history(tip)?; + // Probe for a higher provider limit only after completing a scan. + self.history_range_limit + .store((ceiling * 2).min(MAX_LOG_RANGE), Ordering::Relaxed); return Ok(true); } next = end + 1; @@ -144,7 +167,7 @@ impl UsdtWallet { if tokio::time::Instant::now() >= deadline { return Ok(false); } - let receipt = self.rpc.block_receipt(*hash, &block, number).await?; + let receipt = self.rpc.block_receipt(*hash, block.hash, number).await?; self.save_receipt_history(&id, timestamp, &receipt).await?; } if self.rpc.block(number).await?.hash != block.hash { @@ -191,7 +214,8 @@ impl UsdtWallet { continue; } } - let hash: String = serde_json::from_value(log["transactionHash"].clone())?; + let hash: B256 = serde_json::from_value(log["transactionHash"].clone())?; + let hash = format!("{hash:#x}"); let block = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) .map_err(|_| UsdtError::InvalidResponse)?; if !(start..=end).contains(&block) { @@ -261,6 +285,16 @@ impl UsdtWallet { .rpc .call("eth_getTransactionByHash", json!([hash])) .await?; + if tx.is_null() { + return Err(UsdtError::NetworkUnavailable); + } + if serde_json::from_value::(tx["hash"].clone())? + != hash + .parse::() + .map_err(|_| UsdtError::InvalidResponse)? + { + return Err(UsdtError::InvalidResponse); + } let input: Bytes = serde_json::from_value(tx["input"].clone())?; let target: Option
= serde_json::from_value(tx["to"].clone())?; // A wrapper's outer calldata need not describe the operation it executes. @@ -289,7 +323,10 @@ impl UsdtWallet { }) else { continue; }; - let Some((recipient, amount)) = decode_payment(&op.callData)? else { + if !super::paymaster::supported_payment(&op.paymasterAndData) { + continue; + } + let Some((recipient, amount)) = decode_payment(&op.callData) else { continue; }; (recipient.to_checksum(None), amount) @@ -307,7 +344,7 @@ impl UsdtWallet { timestamp, explorer_url: format!("{EXPLORER}/tx/{hash}"), }; - self.settle(&mut transfer, receipt, event.success)?; + self.settle(&mut transfer, receipt)?; let operation_logs = super::transaction::operation_logs(receipt, event.userOpHash)?; let outgoing_ids: Vec<_> = operation_logs .iter() @@ -323,32 +360,23 @@ impl UsdtWallet { } } -fn decode_payment(data: &[u8]) -> Result, UsdtError> { - let Ok(calls) = decode_calls(data) else { - return Ok(None); - }; +fn decode_payment(data: &[u8]) -> Option<(Address, u64)> { + let calls = decode_calls(data).ok()?; let mut payment = None; - let mut payment_count = 0; - let mut supported = true; for (target, data) in calls { - if target == TOKEN { - if let Ok(call) = Erc20::transferCall::abi_decode(&data) { - payment_count += 1; - let Ok(amount) = token_amount(call.amount) else { - return Ok(None); - }; - payment = Some((call.recipient, amount)); - } else if Erc20::approveCall::abi_decode(&data).is_err() { - supported = false; + if target != TOKEN { + return None; + } + if let Ok(call) = Erc20::transferCall::abi_decode(&data) { + if payment.is_some() { + return None; } - } else { - supported = false; + payment = Some((call.recipient, token_amount(call.amount).ok()?)); + } else if Erc20::approveCall::abi_decode(&data).is_err() { + return None; } } - if !supported || payment_count != 1 { - return Ok(None); - } - Ok(payment) + payment } pub(super) fn decode_calls(data: &[u8]) -> Result, UsdtError> { diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs index cef65e7..f680706 100644 --- a/src/modules/usdt/paymaster.rs +++ b/src/modules/usdt/paymaster.rs @@ -205,11 +205,14 @@ impl Pimlico { return Err(UsdtError::InvalidResponse); } op.paymaster_data = data.paymaster_data; - if let Some(gas) = data.paymaster_verification_gas_limit { - op.paymaster_verification_gas_limit = gas; - } - if let Some(gas) = data.paymaster_post_op_gas_limit { - op.paymaster_post_op_gas_limit = gas; + // Final data signs the estimated limits; only stub data supplies gas estimates. + if method == "pm_getPaymasterStubData" { + if let Some(gas) = data.paymaster_verification_gas_limit { + op.paymaster_verification_gas_limit = gas; + } + if let Some(gas) = data.paymaster_post_op_gas_limit { + op.paymaster_post_op_gas_limit = gas; + } } Ok(()) } @@ -347,6 +350,15 @@ impl Terms { } } +// Packed EntryPoint paymaster data starts after the address and two 16-byte gas limits. +pub(super) fn supported_payment(data: &[u8]) -> bool { + data.get(..20) + .is_some_and(|address| address == PAYMASTER.as_slice()) + && data + .get(52..) + .is_some_and(|terms| Terms::decode(terms).is_ok()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/modules/usdt/payment_request.rs b/src/modules/usdt/payment_request.rs index d28a174..d509f35 100644 --- a/src/modules/usdt/payment_request.rs +++ b/src/modules/usdt/payment_request.rs @@ -7,20 +7,21 @@ use alloy_primitives::Address; #[uniffi::export] pub fn usdt_parse_payment_request(value: String) -> Result { - let (recipient, amount) = parse_request(&value)?; + let (recipient, amount, chain_id) = parse_request(&value)?; Ok(UsdtPaymentRequest { recipient: recipient.to_checksum(None), amount, + chain_id, }) } -fn parse_request(value: &str) -> Result<(Address, Option), UsdtError> { +fn parse_request(value: &str) -> Result<(Address, Option, Option), UsdtError> { let value = value.trim(); if value.len() > 2048 { return Err(UsdtError::InvalidAddress); } let Some((scheme, uri)) = value.split_once(':') else { - return Ok((parse_address(value)?, None)); + return Ok((parse_address(value)?, None, None)); }; if !scheme.eq_ignore_ascii_case("ethereum") { return Err(UsdtError::InvalidAddress); @@ -35,7 +36,7 @@ fn parse_request(value: &str) -> Result<(Address, Option), UsdtError> { if chain != CHAIN_ID.to_string() || !query.is_empty() { return Err(UsdtError::WrongNetwork); } - return Ok((parse_address(address)?, None)); + return Ok((parse_address(address)?, None, Some(CHAIN_ID))); }; if chain != CHAIN_ID.to_string() || parse_address(address)? != TOKEN { return Err(UsdtError::WrongNetwork); @@ -53,5 +54,9 @@ fn parse_request(value: &str) -> Result<(Address, Option), UsdtError> { _ => return Err(UsdtError::InvalidAddress), } } - Ok((recipient.ok_or(UsdtError::InvalidAddress)?, amount)) + Ok(( + recipient.ok_or(UsdtError::InvalidAddress)?, + amount, + Some(CHAIN_ID), + )) } diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs index e667030..5051048 100644 --- a/src/modules/usdt/rpc.rs +++ b/src/modules/usdt/rpc.rs @@ -106,6 +106,7 @@ impl Rpc { "credit", "too many requests", "requests per", + "request rate", "compute units", ] .iter() @@ -113,10 +114,21 @@ impl Rpc { { return Err(UsdtError::RateLimited); } - if method == "eth_getLogs" && error.code == -32005 { + if method == "eth_getLogs" + && (error.code == -32005 + || [ + "block range too", + "block range exceeds", + "query returned too many", + "response size exceeded", + "log query limit", + ] + .iter() + .any(|term| message.contains(term))) + { return Err(UsdtError::LogRangeTooLarge); } - if error.code == -32002 { + if matches!(error.code, -32002 | -32603) { return Err(UsdtError::NetworkUnavailable); } if matches!( @@ -152,14 +164,15 @@ impl Rpc { } pub async fn block(&self, number: u64) -> Result { - self.call("eth_getBlockByNumber", json!([U256::from(number), false])) - .await + self.call::>("eth_getBlockByNumber", json!([U256::from(number), false])) + .await? + .ok_or(UsdtError::NetworkUnavailable) } pub async fn block_receipt( &self, hash: B256, - block: &Block, + block_hash: B256, number: u64, ) -> Result { let receipt: Value = self @@ -169,7 +182,7 @@ impl Rpc { return Err(UsdtError::NetworkUnavailable); } if serde_json::from_value::(receipt["transactionHash"].clone())? != hash - || serde_json::from_value::(receipt["blockHash"].clone())? != block.hash + || serde_json::from_value::(receipt["blockHash"].clone())? != block_hash || serde_json::from_value::(receipt["blockNumber"].clone())? != U256::from(number) { return Err(UsdtError::InvalidResponse); @@ -260,6 +273,10 @@ mod tests { 502, r#"{"error":{"code":-32002,"message":"Provider unavailable"}}"#, ), + ( + 200, + r#"{"error":{"code":-32603,"message":"Internal server error"}}"#, + ), (503, "Service unavailable"), ] { let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs index 3ee9951..91f6214 100644 --- a/src/modules/usdt/store.rs +++ b/src/modules/usdt/store.rs @@ -223,13 +223,6 @@ impl Store { Ok(()) } - pub fn transaction_timestamp(&self, hash: &str) -> Result, UsdtError> { - Ok(self.connection()?.query_row( - "SELECT json_extract(data, '$.timestamp') FROM usdt_transfers WHERE json_extract(data, '$.tx_hash')=?1 AND json_extract(data, '$.status') != 'Pending' LIMIT 1", - [hash], |row| row.get(0), - ).optional()?) - } - pub fn begin_history_block(&self, number: u64, hash: &str) -> Result { let mut connection = self.connection()?; let tx = connection.transaction()?; diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index cd307fc..4e57c99 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -141,6 +141,16 @@ fn payment_request_rejects_wrong_chain_and_malformed_checksum() { usdt_parse_payment_request(request).unwrap().recipient, address ); + assert_eq!( + usdt_parse_payment_request(address.into()).unwrap().chain_id, + None + ); + assert_eq!( + usdt_parse_payment_request(format!("ethereum:{address}@42161")) + .unwrap() + .chain_id, + Some(42161) + ); for invalid in [ format!("ethereum:{address}@42161/transfer?address={address}"), format!("ethereum:{address}@1"), @@ -377,7 +387,20 @@ impl MockChain { if delay { tokio::time::sleep(std::time::Duration::from_secs(6)).await; } - let response = server_state.lock().unwrap().respond(&body).to_string(); + let mut response = server_state.lock().unwrap().respond(&body); + if body["method"] == "eth_getLogs" { + if let Some(logs) = response["result"].as_array_mut() { + for log in logs { + log.as_object_mut().unwrap().entry("blockHash").or_insert( + serde_json::json!(alloy_primitives::B256::repeat_byte(9)), + ); + } + } + } + if body["method"] == "eth_getTransactionByHash" && !response["result"].is_null() { + response["result"]["hash"] = body["params"][0].clone(); + } + let response = response.to_string(); let response=format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{response}",response.len()); let _ = socket.write_all(response.as_bytes()).await; } @@ -476,6 +499,8 @@ impl ChainState { json!({"paymaster":self.paymaster,"paymasterData":Bytes::from(data)}); if body["method"] == "pm_getPaymasterStubData" { response["paymasterPostOpGasLimit"] = json!("0x186a0"); + } else { + response["paymasterVerificationGasLimit"] = json!("0xc350"); } response } @@ -861,48 +886,52 @@ async fn wrong_network_owner_nonce_balance_and_paymaster_cannot_sign() { #[tokio::test] async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { - let chain = MockChain::start().await; - let dir = tempfile::tempdir().unwrap(); - let wallet = chain.wallet(&dir); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000) - .await - .unwrap(); - wallet - .send(quote.id, TEST_PHRASE.into(), None) - .await - .unwrap(); - { - let mut state = chain.state.lock().unwrap(); - state.timestamp += alloy_primitives::U256::from(180); - state.tip += 3; - state.max_log_range = Some(1); - } - assert_eq!( - wallet.refresh_transfers().await.unwrap()[0].status, - UsdtTransferStatus::Pending - ); - assert!(matches!( - wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, - Err(UsdtError::PendingTransfer) - )); - chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(421); - assert_eq!( - wallet.refresh_transfers().await.unwrap()[0].status, - UsdtTransferStatus::Failed - ); - let next = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000) - .await - .unwrap(); - assert_eq!( + for nonce in [0, 1] { + let chain = MockChain::start().await; + chain.state.lock().unwrap().nonce = nonce; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); wallet - .send(next.id, TEST_PHRASE.into(), None) + .send(quote.id, TEST_PHRASE.into(), None) .await - .unwrap() - .status, - UsdtTransferStatus::Pending - ); + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 0; + state.timestamp += alloy_primitives::U256::from(180); + state.tip += 3; + state.max_log_range = Some(1); + } + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert!(matches!( + wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, + Err(UsdtError::PendingTransfer) + )); + chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(421); + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Failed + ); + let next = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + assert_eq!( + wallet + .send(next.id, TEST_PHRASE.into(), None) + .await + .unwrap() + .status, + UsdtTransferStatus::Pending + ); + } } #[tokio::test] @@ -961,13 +990,13 @@ async fn bundled_operations_cannot_contribute_another_payments_fee() { .parse() .unwrap(); let other_hash = B256::repeat_byte(2); - let event = |hash| { + let event = |hash, success| { transaction::EntryPoint::UserOperationEvent { userOpHash: hash, sender: wallet.address, paymaster: paymaster::PAYMASTER, nonce: U256::ZERO, - success: true, + success, actualGasCost: U256::from(1), actualGasUsed: U256::from(1), } @@ -976,18 +1005,20 @@ async fn bundled_operations_cannot_contribute_another_payments_fee() { let log = |address, data: alloy_primitives::LogData| json!({"address":address,"topics":data.topics(),"data":data.data}); let receipt = json!({"logs":[ log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:other_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(500), exchangeRate:U256::from(1) }.encode_log_data()), - log(account::ENTRY_POINT, event(other_hash)), + log(account::ENTRY_POINT, event(other_hash, true)), log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:own_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(123), exchangeRate:U256::from(1) }.encode_log_data()), - log(account::ENTRY_POINT, event(own_hash)), + log(account::ENTRY_POINT, event(own_hash, true)), ]}); assert_eq!( transaction::operation_logs(&receipt, own_hash).unwrap(), &receipt["logs"].as_array().unwrap()[2..] ); - wallet.settle(&mut transfer, &receipt, false).unwrap(); + let mut failed = receipt.clone(); + failed["logs"][3] = log(account::ENTRY_POINT, event(own_hash, false)); + wallet.settle(&mut transfer, &failed).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Failed); assert_eq!(transfer.fee, Some(123)); - wallet.settle(&mut transfer, &receipt, true).unwrap(); + wallet.settle(&mut transfer, &receipt).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Confirmed); assert_eq!(transfer.fee, Some(123)); } @@ -1376,7 +1407,17 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { UsdtTransferStatus::Pending ); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); - chain.state.lock().unwrap().hide_logs = false; + { + let mut state = chain.state.lock().unwrap(); + state.hide_logs = false; + // The discovery log alone is insufficient; recovery verifies the consuming block. + state.hide_receipts = false; + state.receipt_response = Some(serde_json::json!({ + "transactionHash":alloy_primitives::B256::repeat_byte(7), + "blockHash":alloy_primitives::B256::repeat_byte(9), + "blockNumber":alloy_primitives::U256::from(507_000_000),"logs":[] + })); + } assert_eq!( wallet.refresh_transfers().await.unwrap()[0].status, UsdtTransferStatus::Replaced @@ -1401,6 +1442,7 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { for (code, message) in [ (-32005, "Rate limit exceeded"), (-32016, "Provider throttled"), + (-32000, "Request rate exceeded"), (-32000, "Invalid request"), ] { let chain = MockChain::start().await; @@ -1411,7 +1453,7 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { state.log_error = Some((code, message.into())); } let error = wallet.sync_history().await.unwrap_err(); - if code == -32000 { + if message == "Invalid request" { assert!(matches!(error, UsdtError::NetworkUnavailable)); } else { assert!(matches!(error, UsdtError::RateLimited)); @@ -1444,6 +1486,7 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { wallet .history_range_limit .store(1, std::sync::atomic::Ordering::Relaxed); + wallet.store.save_history_progress(19_990).unwrap(); sync_history_to_tip(&wallet).await; assert!( wallet @@ -2065,6 +2108,11 @@ async fn settlement_requires_matching_canonical_receipts() { Err(UsdtError::InvalidResponse) )); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + assert!(matches!( + wallet.sync_history().await, + Err(UsdtError::InvalidResponse) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); } chain.state.lock().unwrap().receipt_response = Some(serde_json::Value::Null); assert!(matches!( @@ -2072,9 +2120,152 @@ async fn settlement_requires_matching_canonical_receipts() { Err(UsdtError::NetworkUnavailable) )); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + assert!(matches!( + wallet.sync_history().await, + Err(UsdtError::NetworkUnavailable) + )); chain.state.lock().unwrap().receipt_response = None; assert_eq!( wallet.refresh_transfers().await.unwrap()[0].status, UsdtTransferStatus::Confirmed ); } + +#[tokio::test] +async fn settlement_uses_the_canonical_operation_outcome() { + use alloy_primitives::{B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + for success in [false, true] { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + let op = &state.operations[0]; + let event = |success| { + transaction::EntryPoint::UserOperationEvent { + userOpHash: op.hash(types::CHAIN_ID).unwrap(), + sender: op.sender, + paymaster: paymaster::PAYMASTER, + nonce: op.nonce, + success, + actualGasCost: U256::from(1), + actualGasUsed: U256::from(1), + } + .encode_log_data() + }; + let receipt_event = event(success); + let log_event = event(!success); + let mut logs = state.event_logs(); + logs[1]["data"] = json!(log_event.data); + state.log_response = Some(vec![logs[1].clone()]); + let mut receipt = state.respond( + &json!({"method":"eth_getTransactionReceipt","params":[B256::repeat_byte(7)]}), + )["result"] + .clone(); + receipt["logs"][1]["data"] = json!(receipt_event.data); + state.receipt_response = Some(receipt); + } + let expected = if success { + UsdtTransferStatus::Confirmed + } else { + UsdtTransferStatus::Failed + }; + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + expected + ); + assert!(wallet.sync_history().await.unwrap()); + assert_eq!(wallet.history().unwrap()[0].status, expected); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); + } +} + +#[tokio::test] +async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { + use alloy_primitives::{Address, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + let movement = |from, to, value, index| { + let event = transaction::Erc20::Transfer { + from, + to, + value: U256::from(value), + } + .encode_log_data(); + json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"logIndex":U256::from(index)}) + }; + for flags in [2u8, 4u8] { + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip = 20003; + let mut data = state.operations[0].paymaster_data.to_vec(); + data[1] = flags; + state.operations[0].paymaster_data = data.into(); + let mut logs = state.event_logs(); + logs.insert( + 0, + movement(wallet.address, paymaster::PAYMASTER, 200u64, 2u64), + ); + logs.insert( + 1, + movement( + wallet.address, + RECIPIENT.parse::
().unwrap(), + 1_000_000u64, + 3u64, + ), + ); + logs.insert( + 2, + movement(paymaster::PAYMASTER, wallet.address, 50u64, 4u64), + ); + state.receipt_logs = Some(logs); + } + let restored_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restored_dir); + assert!(restored.sync_history().await.unwrap()); + let history = restored.history().unwrap(); + assert_eq!(history.len(), 3); + assert!(history.iter().all(|row| row.user_operation_hash.is_none())); + assert_eq!( + history + .iter() + .filter(|row| row.is_incoming) + .map(|row| row.amount) + .sum::(), + 50 + ); + assert_eq!( + history + .iter() + .filter(|row| !row.is_incoming) + .map(|row| row.amount) + .sum::(), + 1_000_200 + ); + } +} diff --git a/src/modules/usdt/types.rs b/src/modules/usdt/types.rs index 744b696..16453c7 100644 --- a/src/modules/usdt/types.rs +++ b/src/modules/usdt/types.rs @@ -9,6 +9,8 @@ pub(super) const EXPLORER: &str = "https://arbiscan.io"; pub struct UsdtPaymentRequest { pub recipient: String, pub amount: Option, + /// An explicit network in the payment URI; bare addresses have no restriction. + pub chain_id: Option, } #[derive(Clone, Debug, Serialize, Deserialize, uniffi::Record)] diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 3f3a38b..429c5cc 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -298,10 +298,8 @@ impl UsdtWallet { if event.userOpHash == hash { self.settle_from_log(&mut transfer, log, event).await?; } else { - transfer.status = UsdtTransferStatus::Replaced; - transfer.received_amount = 0; - transfer.fee = Some(0); - self.store.update_transfer(&transfer)?; + self.reconcile_consumed_nonce(&mut transfer, &plan, block) + .await?; } matched = true; break; @@ -312,12 +310,12 @@ impl UsdtWallet { } } else { let expired = self.block_timestamp(confirmed_tip).await? > plan.expires_at; - if nonce == plan.operation.nonce && expired { + if expired { transfer.status = UsdtTransferStatus::Failed; transfer.received_amount = 0; transfer.fee = Some(0); self.store.update_transfer(&transfer)?; - } else if !expired { + } else { let _ = self.broadcast(&plan, hash).await; } } @@ -345,7 +343,7 @@ impl UsdtWallet { if tokio::time::Instant::now() >= deadline { return Ok(()); } - let receipt = self.rpc.block_receipt(*hash, &block, number).await?; + let receipt = self.rpc.block_receipt(*hash, block.hash, number).await?; for log in receipt["logs"] .as_array() .ok_or(UsdtError::InvalidResponse)? @@ -366,7 +364,7 @@ impl UsdtWallet { } transfer.tx_hash = format!("{hash:#x}"); transfer.explorer_url = format!("{EXPLORER}/tx/{hash:#x}"); - self.settle(transfer, &receipt, event.success)?; + self.settle(transfer, &receipt)?; } else { transfer.status = UsdtTransferStatus::Replaced; transfer.received_amount = 0; @@ -386,6 +384,8 @@ impl UsdtWallet { transfer.status = UsdtTransferStatus::Replaced; transfer.received_amount = 0; transfer.fee = Some(0); + transfer.timestamp = + u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; self.store.update_transfer(transfer) } @@ -394,11 +394,7 @@ impl UsdtWallet { .map_err(|_| UsdtError::InvalidResponse) } pub(super) async fn block_timestamp(&self, number: u64) -> Result { - let block: Value = self - .rpc - .call("eth_getBlockByNumber", json!([U256::from(number), false])) - .await?; - u64::try_from(serde_json::from_value::(block["timestamp"].clone())?) + u64::try_from(self.rpc.block(number).await?.timestamp) .map_err(|_| UsdtError::InvalidResponse) } async fn token_balance(&self) -> Result { @@ -470,17 +466,17 @@ impl UsdtWallet { if event.sender != self.address || event.paymaster != PAYMASTER { return Err(UsdtError::InvalidResponse); } - transfer.tx_hash = serde_json::from_value(log["transactionHash"].clone())?; + let hash: B256 = serde_json::from_value(log["transactionHash"].clone())?; + transfer.tx_hash = format!("{hash:#x}"); transfer.explorer_url = format!("{EXPLORER}/tx/{}", transfer.tx_hash); let number = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) .map_err(|_| UsdtError::InvalidResponse)?; let block = self.rpc.block(number).await?; - let hash = transfer - .tx_hash - .parse() - .map_err(|_| UsdtError::InvalidResponse)?; - let receipt = self.rpc.block_receipt(hash, &block, number).await?; - self.settle(transfer, &receipt, event.success)?; + if serde_json::from_value::(log["blockHash"].clone())? != block.hash { + return Err(UsdtError::NetworkUnavailable); + } + let receipt = self.rpc.block_receipt(hash, block.hash, number).await?; + self.settle(transfer, &receipt)?; transfer.timestamp = u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; self.store.update_transfer(transfer) @@ -522,7 +518,6 @@ impl UsdtWallet { &self, transfer: &mut UsdtTransfer, receipt: &Value, - success: bool, ) -> Result<(), UsdtError> { let operation_hash: B256 = transfer .user_operation_hash @@ -531,6 +526,16 @@ impl UsdtWallet { .parse() .map_err(|_| UsdtError::InvalidResponse)?; let logs = super::transaction::operation_logs(receipt, operation_hash)?; + let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data( + logs.last().ok_or(UsdtError::InvalidResponse)?, + )?) + .map_err(|_| UsdtError::InvalidResponse)?; + if event.userOpHash != operation_hash + || event.sender != self.address + || event.paymaster != PAYMASTER + { + return Err(UsdtError::InvalidResponse); + } let mut gas_fee = None; for log in logs { let address: Address = serde_json::from_value(log["address"].clone())?; @@ -548,7 +553,7 @@ impl UsdtWallet { } } transfer.fee = gas_fee; - transfer.status = if !success { + transfer.status = if !event.success { transfer.received_amount = 0; UsdtTransferStatus::Failed } else { diff --git a/tests/usdt-fork/provider.mjs b/tests/usdt-fork/provider.mjs index 68b730b..65ad95b 100644 --- a/tests/usdt-fork/provider.mjs +++ b/tests/usdt-fork/provider.mjs @@ -148,7 +148,7 @@ async function dispatch(method, params) { const op = { ...params[0], paymaster: pmAddress, ...limits }; const unsigned = concat([ '0x0300', - toBeHex(Math.floor(Date.now() / 1000) + 600, 6), + toBeHex(BigInt((await rpc.send('eth_getBlockByNumber', ['latest', false])).timestamp) + 600n, 6), toBeHex(0, 6), tokenAddress, toBeHex(50000, 16), From 1f805f10918e80a6ea639080a19bad54004760dc Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 25 Sep 2026 11:08:42 +0300 Subject: [PATCH 7/9] fix: check recent USDT execution without scanning history --- Package.swift | 2 +- bindings/ios/bitkitcore.swift | 54 +++++++++++++ bindings/ios/bitkitcoreFFI.h | 11 +++ src/modules/usdt/README.md | 2 + src/modules/usdt/tests.rs | 145 +++++++++++++++++++++++++++++++--- src/modules/usdt/wallet.rs | 59 ++++++++++++++ 6 files changed, 263 insertions(+), 10 deletions(-) diff --git a/Package.swift b/Package.swift index 9cb894f..8a0b1d4 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "7052ccab97f8cb6e85b77d05fd23355970f4d9b81d7b7f37707d2ea0e976781b" +let checksum = "e5a438e4527e549dff46406ca83efccd53aaa0abddb35b01c17ba7d92ccc1bda" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index 2677fb8..bd0bc7d 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -2575,6 +2575,12 @@ public protocol UsdtWalletProtocol: AnyObject, Sendable { func receiveUri() -> String + /** + * Checks recent direct-payment execution at the current tip without scanning history or retrying submission. + * Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. + */ + func refreshTransfer(id: String) async throws -> UsdtTransfer? + func refreshTransfers() async throws -> [UsdtTransfer] func send(quoteId: String, mnemonic: String, passphrase: String?) async throws -> UsdtTransfer @@ -2700,6 +2706,27 @@ open func receiveUri() -> String { }) } + /** + * Checks recent direct-payment execution at the current tip without scanning history or retrying submission. + * Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. + */ +open func refreshTransfer(id: String)async throws -> UsdtTransfer? { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfer( + self.uniffiClonePointer(), + FfiConverterString.lower(id) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterOptionTypeUsdtTransfer.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + open func refreshTransfers()async throws -> [UsdtTransfer] { return try await uniffiRustCallAsync( @@ -24735,6 +24762,30 @@ fileprivate struct FfiConverterOptionTypeTrezorFeatures: FfiConverterRustBuffer } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeUsdtTransfer: FfiConverterRustBuffer { + typealias SwiftType = UsdtTransfer? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeUsdtTransfer.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeUsdtTransfer.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -29686,6 +29737,9 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri() != 33484) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfer() != 58151) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfers() != 32305) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index da7fb99..b9c95d6 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -700,6 +700,11 @@ RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_address(void*_Nonnull RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_uri(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFER +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfer(void*_Nonnull ptr, RustBuffer id +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS uint64_t uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfers(void*_Nonnull ptr @@ -3491,6 +3496,12 @@ uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_address(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_URI uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFER +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfer(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFERS diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index 7f7bef6..e1bce75 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -30,6 +30,8 @@ Seed restoration recovers deposits and outgoing activity from genesis, including `sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Completed fallback scans are retained by canonical block hash within the revisit window. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. +`refresh_transfer` checks one recent direct Arbitrum payment with a five-second request budget. It requires the expected operation outcome and token transfer in a matching canonical receipt and does not scan history, rebroadcast, expire payments or reconcile nonces. It can confirm execution at the current L2 tip; this is provisional sequencer execution, not parent-chain finality. Native send screens may call it approximately once per second during a short foreground window, with cancellation and rate-limit backoff between checks. Missing evidence leaves Pending intact. Normal recovery handles older payments outside its 64-block lookup window. + Scans trail the reported tip by two blocks and revisit 4096 blocks for delayed indexing. This is not reorg rollback: previously recorded orphaned activity is not retracted. Providers must supply complete filtered logs, canonical blocks/receipts and historical state. Payment outcomes and expiry decisions trust the configured chain RPC. A malicious RPC can fabricate or suppress evidence and mislead a user into authorizing another payment; these checks are not light-client proofs. diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index 4e57c99..7ce6b39 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -607,7 +607,11 @@ impl ChainState { && (filter["address"] == json!(account::ENTRY_POINT) || filter["address"].is_array()) { - json!([self.event_logs()[1]]) + json!([self + .event_logs() + .into_iter() + .find(|log| log["address"] == json!(account::ENTRY_POINT.to_checksum(None))) + .unwrap()]) } else { json!([]) } @@ -654,7 +658,7 @@ impl ChainState { } fn event_logs(&self) -> Vec { use alloy_primitives::{B256, U256}; - use alloy_sol_types::SolEvent; + use alloy_sol_types::{SolCall, SolEvent}; use serde_json::json; let op = self.operations.first().unwrap(); let hash = op.hash(types::CHAIN_ID).unwrap(); @@ -681,6 +685,26 @@ impl ChainState { json!({"address":paymaster::PAYMASTER,"topics":gas.topics(),"data":gas.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x0"}), json!({"address":account::ENTRY_POINT.to_checksum(None),"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x1"}), ]; + for (target, data) in history::decode_calls(&op.call_data).unwrap_or_default() { + if target == types::TOKEN { + if let Ok(call) = transaction::Erc20::transferCall::abi_decode(&data) { + if call.amount > U256::from(u64::MAX) { + continue; + } + let payment = transaction::Erc20::Transfer { + from: op.sender, + to: call.recipient, + value: call.amount, + } + .encode_log_data(); + logs.insert(0, json!({"address":types::TOKEN,"topics":payment.topics(),"data":payment.data, + "transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x0"})); + for (index, log) in logs.iter_mut().enumerate() { + log["logIndex"] = json!(format!("0x{index:x}")); + } + } + } + } if !self.mined { logs.clear(); } @@ -691,7 +715,7 @@ impl ChainState { value: U256::from(42), } .encode_log_data(); - logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"logIndex":"0x2"})); + logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"logIndex":format!("0x{:x}", logs.len())})); } if self.external_outgoing { let event = transaction::Erc20::Transfer { @@ -700,7 +724,7 @@ impl ChainState { value: U256::from(77), } .encode_log_data(); - logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":"0x3"})); + logs.push(json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"transactionHash":B256::repeat_byte(7),"blockNumber":"0x4e20","logIndex":format!("0x{:x}", logs.len())})); } logs } @@ -1007,6 +1031,7 @@ async fn bundled_operations_cannot_contribute_another_payments_fee() { log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:other_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(500), exchangeRate:U256::from(1) }.encode_log_data()), log(account::ENTRY_POINT, event(other_hash, true)), log(paymaster::PAYMASTER, transaction::Paymaster::UserOperationSponsored { userOpHash:own_hash, user:wallet.address, paymasterMode:1, token:types::TOKEN, tokenAmountPaid:U256::from(123), exchangeRate:U256::from(1) }.encode_log_data()), + log(types::TOKEN, transaction::Erc20::Transfer { from: wallet.address, to: RECIPIENT.parse().unwrap(), value: U256::from(transfer.amount) }.encode_log_data()), log(account::ENTRY_POINT, event(own_hash, true)), ]}); assert_eq!( @@ -1014,7 +1039,7 @@ async fn bundled_operations_cannot_contribute_another_payments_fee() { &receipt["logs"].as_array().unwrap()[2..] ); let mut failed = receipt.clone(); - failed["logs"][3] = log(account::ENTRY_POINT, event(own_hash, false)); + failed["logs"][4] = log(account::ENTRY_POINT, event(own_hash, false)); wallet.settle(&mut transfer, &failed).unwrap(); assert_eq!(transfer.status, UsdtTransferStatus::Failed); assert_eq!(transfer.fee, Some(123)); @@ -1115,7 +1140,7 @@ async fn history_preserves_receipts_with_external_account_call_shapes() { (single, None, true), (oversized, None, false), (Bytes::from_static(&[1, 2, 3, 4]), None, false), - (original, Some(Bytes::from_static(&[5, 6, 7, 8])), false), + (original, Some(Bytes::from_static(&[5, 6, 7, 8])), true), ] { { let mut state = chain.state.lock().unwrap(); @@ -1178,6 +1203,7 @@ async fn wrapped_history_preserves_signed_payments_and_restores_token_transfers( .into(), )]); let mut logs = state.event_logs(); + logs.retain(|log| log["address"] != json!(types::TOKEN)); for (recipient, amount) in [ (RECIPIENT.parse().unwrap(), 1_000_000), (paymaster::PAYMASTER, 123), @@ -1752,6 +1778,7 @@ async fn seed_restore_includes_external_token_sends_without_duplicate_operation_ if unsupported_batch { let mut state = chain.state.lock().unwrap(); state.mined = true; + state.external_outgoing = false; state.history_input = None; let mut calls = history::decode_calls(&state.operations[0].call_data).unwrap(); calls.push(( @@ -2103,6 +2130,10 @@ async fn settlement_requires_matching_canonical_receipts() { let mut invalid = receipt.clone(); invalid[field] = value; chain.state.lock().unwrap().receipt_response = Some(invalid); + assert!(matches!( + wallet.refresh_transfer(sent.id.clone()).await, + Err(UsdtError::InvalidResponse) + )); assert!(matches!( wallet.refresh_transfers().await, Err(UsdtError::InvalidResponse) @@ -2168,13 +2199,13 @@ async fn settlement_uses_the_canonical_operation_outcome() { let receipt_event = event(success); let log_event = event(!success); let mut logs = state.event_logs(); - logs[1]["data"] = json!(log_event.data); - state.log_response = Some(vec![logs[1].clone()]); + logs[2]["data"] = json!(log_event.data); + state.log_response = Some(vec![logs[2].clone()]); let mut receipt = state.respond( &json!({"method":"eth_getTransactionReceipt","params":[B256::repeat_byte(7)]}), )["result"] .clone(); - receipt["logs"][1]["data"] = json!(receipt_event.data); + receipt["logs"][2]["data"] = json!(receipt_event.data); state.receipt_response = Some(receipt); } let expected = if success { @@ -2226,6 +2257,7 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { data[1] = flags; state.operations[0].paymaster_data = data.into(); let mut logs = state.event_logs(); + logs.retain(|log| log["address"] != json!(types::TOKEN)); logs.insert( 0, movement(wallet.address, paymaster::PAYMASTER, 200u64, 2u64), @@ -2269,3 +2301,98 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { ); } } + +#[tokio::test] +async fn recent_execution_requires_the_expected_token_transfer() { + use alloy_primitives::U256; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + chain.state.lock().unwrap().mined = true; + // Normal recovery still waits for its indexing buffer. + assert_eq!( + wallet.refresh_transfers().await.unwrap()[0].status, + UsdtTransferStatus::Pending + ); + for (recipient, amount) in [ + (RECIPIENT.parse().unwrap(), 999u64), + (wallet.address, sent.amount), + ] { + let mut logs = chain.state.lock().unwrap().event_logs(); + let token = transaction::Erc20::Transfer { + from: wallet.address, + to: recipient, + value: U256::from(amount), + } + .encode_log_data(); + logs[0]["topics"] = json!(token.topics()); + logs[0]["data"] = json!(token.data); + chain.state.lock().unwrap().receipt_logs = Some(logs); + assert!(matches!( + wallet.refresh_transfer(sent.id.clone()).await, + Err(UsdtError::InvalidResponse) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + } + let mut logs = chain.state.lock().unwrap().event_logs(); + logs.remove(0); + chain.state.lock().unwrap().receipt_logs = Some(logs); + assert!(matches!( + wallet.refresh_transfer(sent.id.clone()).await, + Err(UsdtError::InvalidResponse) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + chain.state.lock().unwrap().receipt_logs = None; + let result = wallet + .refresh_transfer(sent.id.clone()) + .await + .unwrap() + .unwrap(); + assert_eq!(result.status, UsdtTransferStatus::Confirmed); + assert_eq!(result.fee, Some(123)); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); +} + +#[tokio::test] +async fn execution_check_preserves_unmined_payments_and_throttling() { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.tip += 3; + state.timestamp += alloy_primitives::U256::from(1000); + } + let result = wallet + .refresh_transfer(sent.id.clone()) + .await + .unwrap() + .unwrap(); + assert_eq!(result.status, UsdtTransferStatus::Pending); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + assert_eq!(chain.state.lock().unwrap().operations.len(), 1); + chain.state.lock().unwrap().log_error = Some((-32016, "rate limit".into())); + assert!(matches!( + wallet.refresh_transfer(sent.id.clone()).await, + Err(UsdtError::RateLimited) + )); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); +} diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 429c5cc..9970d5d 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -212,6 +212,54 @@ impl UsdtWallet { Ok(transfer) } + /// Checks recent direct-payment execution at the current tip without scanning history or retrying submission. + /// Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. + pub async fn refresh_transfer(&self, id: String) -> Result, UsdtError> { + let check = async { + let _guard = self.operation.lock().await; + let Some(mut transfer) = self.store.transfer(&id)? else { + return Ok(None); + }; + let Some(plan) = self.store.pending_plan(&id)? else { + return Ok(Some(transfer)); + }; + self.rpc.verify_chain().await?; + let hash = plan.operation.hash(CHAIN_ID)?; + let tip = self.block_number().await?; + let start = plan.created_block.max(tip.saturating_sub(63)); + if start <= tip { + let logs: Vec = self.rpc.call("eth_getLogs", json!([{ + "address": ENTRY_POINT, "fromBlock": U256::from(start), "toBlock": U256::from(tip), + "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, hash, self.address.into_word()] + }])).await?; + if let Some(log) = logs + .iter() + .find(|log| log["removed"].as_bool() != Some(true)) + { + let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) + .map_err(|_| UsdtError::InvalidResponse)?; + let number = + u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) + .map_err(|_| UsdtError::InvalidResponse)?; + if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT + || event.userOpHash != hash + || event.nonce != plan.operation.nonce + || number < start + || number > tip + { + return Err(UsdtError::InvalidResponse); + } + self.settle_from_log(&mut transfer, log, event).await?; + } + } + Ok(Some(transfer)) + }; + match tokio::time::timeout(std::time::Duration::from_secs(5), check).await { + Ok(result) => result, + Err(_) => Err(UsdtError::NetworkUnavailable), + } + } + pub async fn sync_history(&self) -> Result { let _guard = self.operation.lock().await; self.rpc.verify_chain().await?; @@ -536,10 +584,18 @@ impl UsdtWallet { { return Err(UsdtError::InvalidResponse); } + let mut transfer_proven = false; let mut gas_fee = None; for log in logs { let address: Address = serde_json::from_value(log["address"].clone())?; let data = event_data(log)?; + if address == TOKEN { + if let Ok(payment) = Erc20::Transfer::decode_log_data(&data) { + transfer_proven |= payment.from == self.address + && payment.to == parse_address(&transfer.recipient)? + && payment.value == U256::from(transfer.amount); + } + } if address == PAYMASTER { if let Ok(event) = Paymaster::UserOperationSponsored::decode_log_data(&data) { if event.userOpHash == operation_hash @@ -552,6 +608,9 @@ impl UsdtWallet { } } } + if event.success && !transfer_proven { + return Err(UsdtError::InvalidResponse); + } transfer.fee = gas_fee; transfer.status = if !event.success { transfer.received_amount = 0; From f74eebe64dd7fee31169b781887cd87c5afec5ac Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 25 Sep 2026 16:18:58 +0300 Subject: [PATCH 8/9] refactor: simplify USDT recovery and history handling --- Package.swift | 2 +- bindings/ios/bitkitcore.swift | 123 ++++++---- bindings/ios/bitkitcoreFFI.h | 22 +- src/modules/usdt/README.md | 12 +- src/modules/usdt/amount.rs | 37 +++ src/modules/usdt/errors.rs | 2 +- src/modules/usdt/history.rs | 183 +++++++------- src/modules/usdt/keys.rs | 12 + src/modules/usdt/paymaster.rs | 27 +- src/modules/usdt/rpc.rs | 19 +- src/modules/usdt/store.rs | 145 ++++++----- src/modules/usdt/tests.rs | 421 ++++++++++++++++++++++++++------ src/modules/usdt/transaction.rs | 24 +- src/modules/usdt/types.rs | 17 +- src/modules/usdt/wallet.rs | 305 +++++++++++------------ 15 files changed, 880 insertions(+), 471 deletions(-) diff --git a/Package.swift b/Package.swift index 8a0b1d4..8417027 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "e5a438e4527e549dff46406ca83efccd53aaa0abddb35b01c17ba7d92ccc1bda" +let checksum = "0468d0d735745d36079ea0e7f03cb73b3c284376a296cdb2c7836e511a2a284b" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index bd0bc7d..5b7f48d 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -2567,6 +2567,13 @@ public protocol UsdtWalletProtocol: AnyObject, Sendable { func balance() async throws -> UInt64 + /** + * Checks recent direct-payment execution with a bounded request budget. + * Requires the expected operation and transfer in a canonical receipt; current-tip execution is provisional. + * Does not rebroadcast, expire payments or reconcile nonces. Missing evidence leaves the payment pending. + */ + func checkRecentExecution(id: String) async throws -> UsdtTransfer? + func history() throws -> [UsdtTransfer] func quoteTransfer(recipient: String, amount: UInt64) async throws -> UsdtQuote @@ -2576,15 +2583,20 @@ public protocol UsdtWalletProtocol: AnyObject, Sendable { func receiveUri() -> String /** - * Checks recent direct-payment execution at the current tip without scanning history or retrying submission. - * Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. + * Reconciles pending execution using chain proofs and may rebroadcast the identical signed operation. */ - func refreshTransfer(id: String) async throws -> UsdtTransfer? - func refreshTransfers() async throws -> [UsdtTransfer] + /** + * Repeating a quote ID returns its stored outcome, which may already be failed or replaced. + * A pending outcome is durable and retryable; it does not imply bundler acceptance. + */ func send(quoteId: String, mnemonic: String, passphrase: String?) async throws -> UsdtTransfer + /** + * Saves resumable history progress; returns true when caught up and false when more work remains. + * Call between send flows. The soft budget permits an in-flight receipt to finish before yielding. + */ func syncHistory() async throws -> Bool } @@ -2627,6 +2639,9 @@ open class UsdtWallet: UsdtWalletProtocol, @unchecked Sendable { public func uniffiClonePointer() -> UnsafeMutableRawPointer { return try! rustCall { uniffi_bitkitcore_fn_clone_usdtwallet(self.pointer, $0) } } + /** + * Creates the sole owner of this wallet's database; reuse it for all calls until it is dropped. + */ public convenience init(address: String, storagePath: String, rpcUrl: String, bundlerUrl: String)throws { let pointer = try rustCallWithError(FfiConverterTypeUsdtError_lift) { @@ -2668,6 +2683,28 @@ open func balance()async throws -> UInt64 { ) } + /** + * Checks recent direct-payment execution with a bounded request budget. + * Requires the expected operation and transfer in a canonical receipt; current-tip execution is provisional. + * Does not rebroadcast, expire payments or reconcile nonces. Missing evidence leaves the payment pending. + */ +open func checkRecentExecution(id: String)async throws -> UsdtTransfer? { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_method_usdtwallet_check_recent_execution( + self.uniffiClonePointer(), + FfiConverterString.lower(id) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterOptionTypeUsdtTransfer.lift, + errorHandler: FfiConverterTypeUsdtError_lift + ) +} + open func history()throws -> [UsdtTransfer] { return try FfiConverterSequenceTypeUsdtTransfer.lift(try rustCallWithError(FfiConverterTypeUsdtError_lift) { uniffi_bitkitcore_fn_method_usdtwallet_history(self.uniffiClonePointer(),$0 @@ -2707,26 +2744,8 @@ open func receiveUri() -> String { } /** - * Checks recent direct-payment execution at the current tip without scanning history or retrying submission. - * Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. + * Reconciles pending execution using chain proofs and may rebroadcast the identical signed operation. */ -open func refreshTransfer(id: String)async throws -> UsdtTransfer? { - return - try await uniffiRustCallAsync( - rustFutureFunc: { - uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfer( - self.uniffiClonePointer(), - FfiConverterString.lower(id) - ) - }, - pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, - completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, - freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, - liftFunc: FfiConverterOptionTypeUsdtTransfer.lift, - errorHandler: FfiConverterTypeUsdtError_lift - ) -} - open func refreshTransfers()async throws -> [UsdtTransfer] { return try await uniffiRustCallAsync( @@ -2744,6 +2763,10 @@ open func refreshTransfers()async throws -> [UsdtTransfer] { ) } + /** + * Repeating a quote ID returns its stored outcome, which may already be failed or replaced. + * A pending outcome is durable and retryable; it does not imply bundler acceptance. + */ open func send(quoteId: String, mnemonic: String, passphrase: String?)async throws -> UsdtTransfer { return try await uniffiRustCallAsync( @@ -2761,6 +2784,10 @@ open func send(quoteId: String, mnemonic: String, passphrase: String?)async thro ) } + /** + * Saves resumable history progress; returns true when caught up and false when more work remains. + * Call between send flows. The soft budget permits an in-flight receipt to finish before yielding. + */ open func syncHistory()async throws -> Bool { return try await uniffiRustCallAsync( @@ -16114,7 +16141,10 @@ public func FfiConverterTypeUsdtQuote_lower(_ value: UsdtQuote) -> RustBuffer { public struct UsdtTransfer { public var id: String - public var txHash: String + /** + * Source transaction hash, absent until execution is observed. + */ + public var txHash: String? public var userOperationHash: String? public var recipient: String public var amount: UInt64 @@ -16123,11 +16153,13 @@ public struct UsdtTransfer { public var isIncoming: Bool public var status: UsdtTransferStatus public var timestamp: UInt64 - public var explorerUrl: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, txHash: String, userOperationHash: String?, recipient: String, amount: UInt64, receivedAmount: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64, explorerUrl: String) { + public init(id: String, + /** + * Source transaction hash, absent until execution is observed. + */txHash: String?, userOperationHash: String?, recipient: String, amount: UInt64, receivedAmount: UInt64, fee: UInt64?, isIncoming: Bool, status: UsdtTransferStatus, timestamp: UInt64) { self.id = id self.txHash = txHash self.userOperationHash = userOperationHash @@ -16138,7 +16170,6 @@ public struct UsdtTransfer { self.isIncoming = isIncoming self.status = status self.timestamp = timestamp - self.explorerUrl = explorerUrl } } @@ -16179,9 +16210,6 @@ extension UsdtTransfer: Equatable, Hashable { if lhs.timestamp != rhs.timestamp { return false } - if lhs.explorerUrl != rhs.explorerUrl { - return false - } return true } @@ -16196,7 +16224,6 @@ extension UsdtTransfer: Equatable, Hashable { hasher.combine(isIncoming) hasher.combine(status) hasher.combine(timestamp) - hasher.combine(explorerUrl) } } @@ -16212,7 +16239,7 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { return try UsdtTransfer( id: FfiConverterString.read(from: &buf), - txHash: FfiConverterString.read(from: &buf), + txHash: FfiConverterOptionString.read(from: &buf), userOperationHash: FfiConverterOptionString.read(from: &buf), recipient: FfiConverterString.read(from: &buf), amount: FfiConverterUInt64.read(from: &buf), @@ -16220,14 +16247,13 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { fee: FfiConverterOptionUInt64.read(from: &buf), isIncoming: FfiConverterBool.read(from: &buf), status: FfiConverterTypeUsdtTransferStatus.read(from: &buf), - timestamp: FfiConverterUInt64.read(from: &buf), - explorerUrl: FfiConverterString.read(from: &buf) + timestamp: FfiConverterUInt64.read(from: &buf) ) } public static func write(_ value: UsdtTransfer, into buf: inout [UInt8]) { FfiConverterString.write(value.id, into: &buf) - FfiConverterString.write(value.txHash, into: &buf) + FfiConverterOptionString.write(value.txHash, into: &buf) FfiConverterOptionString.write(value.userOperationHash, into: &buf) FfiConverterString.write(value.recipient, into: &buf) FfiConverterUInt64.write(value.amount, into: &buf) @@ -16236,7 +16262,6 @@ public struct FfiConverterTypeUsdtTransfer: FfiConverterRustBuffer { FfiConverterBool.write(value.isIncoming, into: &buf) FfiConverterTypeUsdtTransferStatus.write(value.status, into: &buf) FfiConverterUInt64.write(value.timestamp, into: &buf) - FfiConverterString.write(value.explorerUrl, into: &buf) } } @@ -23557,9 +23582,21 @@ extension UsdtError: Foundation.LocalizedError { public enum UsdtTransferStatus { + /** + * Signed payment awaiting a conclusive source-chain outcome. + */ case pending + /** + * Payment received on its destination chain. + */ case confirmed + /** + * Source payment failed or was proven not to have executed. + */ case failed + /** + * Another operation consumed the payment nonce. + */ case replaced } @@ -29725,6 +29762,9 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_method_usdtwallet_balance() != 12328) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_method_usdtwallet_check_recent_execution() != 33172) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_method_usdtwallet_history() != 4617) { return InitializationResult.apiChecksumMismatch } @@ -29737,22 +29777,19 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri() != 33484) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfer() != 58151) { - return InitializationResult.apiChecksumMismatch - } - if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfers() != 32305) { + if (uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfers() != 34299) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_method_usdtwallet_send() != 10847) { + if (uniffi_bitkitcore_checksum_method_usdtwallet_send() != 60030) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_method_usdtwallet_sync_history() != 48106) { + if (uniffi_bitkitcore_checksum_method_usdtwallet_sync_history() != 25445) { return InitializationResult.apiChecksumMismatch } if (uniffi_bitkitcore_checksum_constructor_urdecoder_new() != 23014) { return InitializationResult.apiChecksumMismatch } - if (uniffi_bitkitcore_checksum_constructor_usdtwallet_new() != 63633) { + if (uniffi_bitkitcore_checksum_constructor_usdtwallet_new() != 62148) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index b9c95d6..63b655a 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -680,6 +680,11 @@ void*_Nonnull uniffi_bitkitcore_fn_constructor_usdtwallet_new(RustBuffer address uint64_t uniffi_bitkitcore_fn_method_usdtwallet_balance(void*_Nonnull ptr ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_CHECK_RECENT_EXECUTION +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_CHECK_RECENT_EXECUTION +uint64_t uniffi_bitkitcore_fn_method_usdtwallet_check_recent_execution(void*_Nonnull ptr, RustBuffer id +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_HISTORY #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_HISTORY RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_history(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status @@ -700,11 +705,6 @@ RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_address(void*_Nonnull RustBuffer uniffi_bitkitcore_fn_method_usdtwallet_receive_uri(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status ); #endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFER -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFER -uint64_t uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfer(void*_Nonnull ptr, RustBuffer id -); -#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_METHOD_USDTWALLET_REFRESH_TRANSFERS uint64_t uniffi_bitkitcore_fn_method_usdtwallet_refresh_transfers(void*_Nonnull ptr @@ -3472,6 +3472,12 @@ uint16_t uniffi_bitkitcore_checksum_method_urdecoder_reset(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_BALANCE uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_balance(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_CHECK_RECENT_EXECUTION +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_CHECK_RECENT_EXECUTION +uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_check_recent_execution(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_HISTORY @@ -3496,12 +3502,6 @@ uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_address(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_RECEIVE_URI uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_receive_uri(void -); -#endif -#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFER -#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFER -uint16_t uniffi_bitkitcore_checksum_method_usdtwallet_refresh_transfer(void - ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_METHOD_USDTWALLET_REFRESH_TRANSFERS diff --git a/src/modules/usdt/README.md b/src/modules/usdt/README.md index e1bce75..aaa8162 100644 --- a/src/modules/usdt/README.md +++ b/src/modules/usdt/README.md @@ -24,19 +24,19 @@ The pinned ERC-20 paymaster collects USDT. Its finite approval includes a 5% mar Signed operations persist atomically before submission. Lost or rejected submission responses do not prove nonexecution: recovery retries only the identical signed operation. A quote ID cannot authorize a second payment. One source-chain payment remains pending at a time. -A matching event in a canonical receipt settles the payment. Discovery logs alone never decide the outcome. Expired signed paymaster terms and a confirmed EntryPoint nonce that has not passed the signed nonce release an unmined operation; the shorter quote deadline does not. With an advanced nonce and missing indexed events, recovery checks every receipt in the consuming block. A matching event settles/replaces the payment; complete absence proves external nonce consumption. Missing receipts preserve the pending operation. Progress is stored by payment and block hash so interruption does not restart the proof or carry it onto another block. +A matching event in a canonical receipt settles the payment. Discovery logs alone never decide the outcome; unavailable log queries allow independent nonce/receipt proofs to proceed, while rate limits retain backoff. Expired signed paymaster terms and a confirmed EntryPoint nonce that has not passed the signed nonce release an unmined operation; the shorter quote deadline does not. With an advanced nonce and missing indexed events, recovery checks every receipt in the consuming block. A matching event settles/replaces the payment; complete absence proves external nonce consumption. Missing receipts preserve the pending operation. Progress is stored by payment and block hash so interruption does not restart the proof or carry it onto another block. -Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls and paymaster modes recover payment/fee attribution; unknown wrappers or payment modes preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. +Seed restoration recovers deposits and outgoing activity from genesis, including transfers before delegation and sends through another wallet. Supported direct EntryPoint calls and paymaster modes recover payment/fee attribution; unknown wrappers or payment modes preserve raw token transfers instead of guessing their intent. Failed payments retain attempted amounts but have no delivered amount. Transaction hashes are absent until execution is observed; callers derive explorer links from the source transaction hash rather than storing a second copy of it. -`sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. A single-block log overflow falls back to that block's individual receipts. Completed fallback scans are retained by canonical block hash within the revisit window. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. +`sync_history` returns `true` when caught up and `false` when more work remains. It uses adaptive log ranges and a 20-second soft budget between persisted receipts; an in-flight receipt may finish later. Learned range limits survive budget exits. A single-block log overflow falls back to that block's individual receipts. Complete receipt enrichment and fallback scans are retained by canonical block hash within the revisit window; log-only incoming observations remain eligible for later enrichment. Zero/self transfers are discarded before enrichment. Network failures preserve completed work and never silently skip a block. Callers should defer catch-up during payment review/submission so historical work does not delay sends. -`refresh_transfer` checks one recent direct Arbitrum payment with a five-second request budget. It requires the expected operation outcome and token transfer in a matching canonical receipt and does not scan history, rebroadcast, expire payments or reconcile nonces. It can confirm execution at the current L2 tip; this is provisional sequencer execution, not parent-chain finality. Native send screens may call it approximately once per second during a short foreground window, with cancellation and rate-limit backoff between checks. Missing evidence leaves Pending intact. Normal recovery handles older payments outside its 64-block lookup window. +`check_recent_execution` checks one recent direct Arbitrum payment with a five-second request budget. It requires the expected operation outcome and token transfer in a matching canonical receipt and does not scan history, rebroadcast, expire payments or reconcile nonces. It can confirm execution at the current L2 tip; this is provisional sequencer execution, not parent-chain finality. Native send screens may call it approximately once per second during a short foreground window, with cancellation and rate-limit backoff between checks. Missing evidence leaves Pending intact. Normal recovery handles older payments outside its 64-block lookup window. -Scans trail the reported tip by two blocks and revisit 4096 blocks for delayed indexing. This is not reorg rollback: previously recorded orphaned activity is not retracted. Providers must supply complete filtered logs, canonical blocks/receipts and historical state. +Scans trail the reported tip by two blocks and revisit 4096 blocks for delayed indexing. This is not reorg rollback: previously recorded orphaned activity is not retracted. Providers must supply complete filtered logs, canonical blocks/receipts and historical state. The first scan starts at block zero, including pre-Nitro ranges; providers must answer those queries or return a supported range-limit error so the scan can narrow them. Logs first indexed more than 4096 blocks late can fall outside the revisit window. This is a block-count limit, not a guaranteed time interval or a measured provider indexing guarantee. Payment outcomes and expiry decisions trust the configured chain RPC. A malicious RPC can fabricate or suppress evidence and mislead a user into authorizing another payment; these checks are not light-client proofs. -Storage is wallet-specific and owned by the `UsdtWallet` object. Drop it before deleting its database during an explicit wallet wipe. Async exports use UniFFI's Tokio adapter, preserving cancellation of the polled future; they do not detach sends onto the global runtime used by stateless exports. +Storage is wallet-specific and must have one owning `UsdtWallet` object. Drop it before deleting its database during an explicit wallet wipe. Async exports use UniFFI's Tokio adapter; Kotlin cancellation can drop the polled future, whereas the current Swift bindings may finish an in-flight call after task cancellation. Callers must check cancellation between calls. Sends are not detached onto the global runtime used by stateless exports. ## Transport diff --git a/src/modules/usdt/amount.rs b/src/modules/usdt/amount.rs index a99cc1c..50aaf68 100644 --- a/src/modules/usdt/amount.rs +++ b/src/modules/usdt/amount.rs @@ -33,6 +33,19 @@ pub fn usdt_format_amount(amount: u64) -> String { .to_string() } +pub(super) fn with_margin(value: U256, percent: u8) -> Result { + let hundred = U256::from(100); + let percent = U256::from(percent); + let margin = (value / hundred) + .checked_mul(percent) + .and_then(|margin| margin.checked_add(value % hundred * percent / hundred)) + .ok_or(UsdtError::InvalidResponse)?; + value + .checked_add(margin) + .and_then(|value| value.checked_add(U256::from(1))) + .ok_or(UsdtError::InvalidResponse) +} + pub(super) fn token_amount(value: U256) -> Result { value.try_into().map_err(|_| UsdtError::InvalidAmount) } @@ -73,3 +86,27 @@ pub(super) fn parse_atomic_amount(value: &str) -> Result { .checked_mul(10u64.checked_pow(power).ok_or(UsdtError::InvalidAmount)?) .ok_or(UsdtError::InvalidAmount) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn margins_preserve_rounding_without_intermediate_overflow() { + for percent in [5, 10, 20] { + for value in [ + U256::ZERO, + U256::from(19), + U256::from(20), + U256::MAX / U256::from(2), + ] { + let expected = value + value / U256::from(100 / percent) + U256::from(1); + assert_eq!(with_margin(value, percent).unwrap(), expected); + } + assert!(matches!( + with_margin(U256::MAX, percent), + Err(UsdtError::InvalidResponse) + )); + } + } +} diff --git a/src/modules/usdt/errors.rs b/src/modules/usdt/errors.rs index 688d7b8..3a5cc80 100644 --- a/src/modules/usdt/errors.rs +++ b/src/modules/usdt/errors.rs @@ -6,7 +6,7 @@ pub enum UsdtError { InvalidAmount, #[error("Enter a valid address for the selected network")] InvalidAddress, - #[error("The payment request is for a different network or token")] + #[error("The network or token does not match this USDT account")] WrongNetwork, #[error("Wallet credentials do not match this USDT account")] InvalidCredentials, diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index d641435..e198538 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -1,8 +1,8 @@ use super::{ account::{SimpleAccount, ENTRY_POINT}, amount::token_amount, - transaction::{event_data, EntryPoint, Erc20}, - types::{EXPLORER, TOKEN}, + transaction::{entry_point_event, event_data, EntryPoint, Erc20}, + types::TOKEN, UsdtError, UsdtTransfer, UsdtTransferStatus, UsdtWallet, }; use alloy_primitives::{Address, Bytes, B256, U256}; @@ -11,15 +11,18 @@ use serde_json::{json, Value}; use std::{collections::BTreeMap, sync::atomic::Ordering}; pub(super) const MAX_LOG_RANGE: u64 = 10_000_000; +pub(super) const HISTORY_REVISIT_BLOCKS: u64 = 4096; +const HISTORY_BUDGET: std::time::Duration = std::time::Duration::from_secs(20); +const LOG_QUERY_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); impl UsdtWallet { pub(super) async fn scan_history(&self) -> Result { - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(20); + let deadline = tokio::time::Instant::now() + HISTORY_BUDGET; let tip = self.block_number().await?.saturating_sub(2); let previous = self.store.synced_block()?; let start = self.store.history_progress()?.unwrap_or_else(|| { previous - .map(|block| block.saturating_sub(4096)) + .map(|block| block.saturating_sub(HISTORY_REVISIT_BLOCKS)) .unwrap_or(0) }); if start > tip { @@ -29,79 +32,22 @@ impl UsdtWallet { let mut ceiling = initial_limit; let mut next = start; let mut width = initial_limit.min(tip - start + 1); - while next <= tip { + loop { if tokio::time::Instant::now() >= deadline { - self.history_range_limit - .store((ceiling * 2).min(MAX_LOG_RANGE), Ordering::Relaxed); + self.history_range_limit.store(ceiling, Ordering::Relaxed); return Ok(false); } let end = next.saturating_add(width - 1).min(tip); - let query = tokio::time::timeout( - std::time::Duration::from_secs(10), - self.history_logs(next, end), - ) - .await; - let result = match query { - Ok(result) => result, - Err(_) => { - self.history_range_limit - .store((width / 2).max(1), Ordering::Relaxed); - return if next > start { - Ok(false) - } else { - Err(UsdtError::NetworkUnavailable) - }; - } - }; + let result = tokio::time::timeout(LOG_QUERY_TIMEOUT, self.history_logs(next, end)) + .await + .unwrap_or(Err(UsdtError::NetworkUnavailable)); match result { Ok(transactions) => { if self.store.history_progress()? != Some(next) { self.store.save_history_progress(next)?; } - let mut blocks = BTreeMap::new(); - for ((block, hash), logs) in transactions { - if self.store.has_history_receipt(&hash)? { - continue; - } - if tokio::time::Instant::now() >= deadline { - return Ok(false); - } - let needs_receipt = logs.iter().any(|log| { - serde_json::from_value::
(log["address"].clone()) - .is_ok_and(|address| address == ENTRY_POINT) - || event_data(log) - .ok() - .and_then(|data| Erc20::Transfer::decode_log_data(&data).ok()) - .is_some_and(|event| event.from == self.address) - }); - let canonical = match blocks.entry(block) { - std::collections::btree_map::Entry::Occupied(entry) => entry.into_mut(), - std::collections::btree_map::Entry::Vacant(entry) => { - entry.insert(self.rpc.block(block).await?) - } - }; - for log in &logs { - if serde_json::from_value::(log["blockHash"].clone())? - != canonical.hash - { - return Err(UsdtError::NetworkUnavailable); - } - } - let receipt = if needs_receipt { - self.rpc - .block_receipt( - hash.parse().map_err(|_| UsdtError::InvalidResponse)?, - canonical.hash, - block, - ) - .await? - } else { - json!({"logs": logs}) - }; - let timestamp = u64::try_from(canonical.timestamp) - .map_err(|_| UsdtError::InvalidResponse)?; - self.save_receipt_history(&hash, timestamp, &receipt) - .await?; + if !self.scan_history_logs(transactions, end, deadline).await? { + return Ok(false); } } Err(UsdtError::LogRangeTooLarge) if next < end => { @@ -142,6 +88,66 @@ impl UsdtWallet { self.history_range_limit.store(width, Ordering::Relaxed); width = width.min(tip - next + 1); } + } + + async fn scan_history_logs( + &self, + transactions: BTreeMap<(u64, String), Vec>, + end: u64, + deadline: tokio::time::Instant, + ) -> Result { + let mut blocks = BTreeMap::new(); + let mut transactions = transactions.into_iter().peekable(); + while let Some(((block, hash), logs)) = transactions.next() { + if tokio::time::Instant::now() >= deadline { + return Ok(false); + } + let needs_receipt = logs.iter().any(|log| { + serde_json::from_value::
(log["address"].clone()) + .is_ok_and(|address| address == ENTRY_POINT) + || event_data(log) + .ok() + .and_then(|data| Erc20::Transfer::decode_log_data(&data).ok()) + .is_some_and(|event| event.from == self.address) + }); + let canonical = match blocks.entry(block) { + std::collections::btree_map::Entry::Occupied(entry) => entry.into_mut(), + std::collections::btree_map::Entry::Vacant(entry) => { + entry.insert(self.rpc.block(block).await?) + } + }; + for log in &logs { + if serde_json::from_value::(log["blockHash"].clone())? != canonical.hash { + return Err(UsdtError::NetworkUnavailable); + } + } + if !self.store.has_history_receipt( + &hash, + &format!("{:#x}", canonical.hash), + needs_receipt, + )? { + let receipt = if needs_receipt { + self.rpc + .block_receipt( + hash.parse().map_err(|_| UsdtError::InvalidResponse)?, + canonical.hash, + block, + ) + .await? + } else { + json!({"logs": logs}) + }; + self.save_receipt_history(&hash, block, canonical, &receipt, needs_receipt) + .await?; + } + if block < end + && transactions + .peek() + .is_none_or(|((next_block, _), _)| *next_block != block) + { + self.store.save_history_progress(block + 1)?; + } + } Ok(true) } @@ -155,20 +161,20 @@ impl UsdtWallet { if self.store.begin_history_block(number, &block_hash)? { return Ok(true); } - let timestamp = u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; if self.store.history_progress()? != Some(number) { self.store.save_history_progress(number)?; } for hash in &block.transactions { let id = format!("{hash:#x}"); - if self.store.has_history_receipt(&id)? { + if self.store.has_history_receipt(&id, &block_hash, true)? { continue; } if tokio::time::Instant::now() >= deadline { return Ok(false); } let receipt = self.rpc.block_receipt(*hash, block.hash, number).await?; - self.save_receipt_history(&id, timestamp, &receipt).await?; + self.save_receipt_history(&id, number, &block, &receipt, true) + .await?; } if self.rpc.block(number).await?.hash != block.hash { return Err(UsdtError::NetworkUnavailable); @@ -180,12 +186,21 @@ impl UsdtWallet { async fn save_receipt_history( &self, hash: &str, - timestamp: u64, + number: u64, + block: &super::rpc::Block, receipt: &Value, + complete_receipt: bool, ) -> Result<(), UsdtError> { // Finish and persist a receipt before yielding the work budget. + let timestamp = block.timestamp()?; let transfers = self.receipt_history(hash, timestamp, receipt).await?; - self.store.save_history_receipt(&transfers, hash) + self.store.save_history_receipt( + &transfers, + hash, + number, + &format!("{:#x}", block.hash), + complete_receipt, + ) } async fn history_logs( @@ -239,16 +254,12 @@ impl UsdtWallet { let mut owned_operations = Vec::new(); for log in logs { let address: Address = serde_json::from_value(log["address"].clone())?; - if address == ENTRY_POINT { - if let Ok(event) = - EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) - { - if event.sender == self.address { - let saved = self - .store - .transfer_by_hash(&format!("{:#x}", event.userOpHash))?; - owned_operations.push((event, saved)); - } + if let Some(event) = entry_point_event(log)? { + if event.sender == self.address { + let saved = self + .store + .transfer_by_hash(&format!("{:#x}", event.userOpHash))?; + owned_operations.push((event, saved)); } } if address != TOKEN { @@ -265,7 +276,7 @@ impl UsdtWallet { let index: U256 = serde_json::from_value(log["logIndex"].clone())?; result.push(UsdtTransfer { id: format!("{hash}:{index}"), - tx_hash: hash.into(), + tx_hash: Some(hash.into()), user_operation_hash: None, recipient: event.to.to_checksum(None), amount: token_amount(event.value)?, @@ -274,7 +285,6 @@ impl UsdtWallet { is_incoming: incoming, status: UsdtTransferStatus::Confirmed, timestamp, - explorer_url: format!("{EXPLORER}/tx/{hash}"), }); } if owned_operations.is_empty() { @@ -333,7 +343,7 @@ impl UsdtWallet { }; let mut transfer = UsdtTransfer { id: operation_hash.clone(), - tx_hash: hash.into(), + tx_hash: Some(hash.into()), user_operation_hash: Some(operation_hash), recipient, amount, @@ -342,7 +352,6 @@ impl UsdtWallet { is_incoming: false, status: UsdtTransferStatus::Pending, timestamp, - explorer_url: format!("{EXPLORER}/tx/{hash}"), }; self.settle(&mut transfer, receipt)?; let operation_logs = super::transaction::operation_logs(receipt, event.userOpHash)?; diff --git a/src/modules/usdt/keys.rs b/src/modules/usdt/keys.rs index f338575..414885f 100644 --- a/src/modules/usdt/keys.rs +++ b/src/modules/usdt/keys.rs @@ -45,6 +45,18 @@ pub(super) fn key_address(key: &SecretKey) -> Address { Address::from_raw_public_key(&public[1..]) } +pub(super) fn derive_owner_key( + mnemonic: Zeroizing, + passphrase: Option>, + owner: Address, +) -> Result { + let key = derive_key(mnemonic, passphrase)?; + if key_address(&key) != owner { + return Err(UsdtError::InvalidCredentials); + } + Ok(key) +} + #[uniffi::export] pub fn usdt_address(mnemonic: String, passphrase: Option) -> Result { Ok(key_address(&*derive_key(mnemonic.into(), passphrase.map(Into::into))?).to_checksum(None)) diff --git a/src/modules/usdt/paymaster.rs b/src/modules/usdt/paymaster.rs index f680706..ea1c292 100644 --- a/src/modules/usdt/paymaster.rs +++ b/src/modules/usdt/paymaster.rs @@ -1,5 +1,6 @@ use super::{ account::ENTRY_POINT, + amount::with_margin, rpc::Rpc, transaction::Erc20, user_operation::{Authorization, UserOperation}, @@ -12,6 +13,8 @@ use serde_json::json; use super::types::{CHAIN_ID as ARBITRUM_CHAIN_ID, TOKEN as USDT0}; pub(super) const PAYMASTER: Address = address!("888888888888Ec68A58AB8094Cc1AD20Ba3D2402"); +const MIN_PAYMASTER_VALIDITY_SECONDS: u64 = 15; +const MAX_PAYMASTER_VALIDITY_SECONDS: u64 = 900; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] @@ -63,10 +66,7 @@ impl GasEstimate { (&mut op.pre_verification_gas, self.pre_verification_gas), ] { if estimate > *limit { - *limit = estimate - .checked_add(estimate / U256::from(10)) - .and_then(|value| value.checked_add(U256::from(1))) - .ok_or(UsdtError::InvalidResponse)?; + *limit = with_margin(estimate, 10)?; changed = true; } } @@ -158,7 +158,7 @@ impl Pimlico { valid_until: 0, valid_after: 0, }; - let mut allowance = approval_margin(estimate_terms.maximum_token_cost(&op)?)?; + let mut allowance = with_margin(estimate_terms.maximum_token_cost(&op)?, 5)?; // Provider data can change gas limits and token charges. Refine both before signing. for _ in 0..3 { op.call_data = with_approval(calls, allowance); @@ -171,13 +171,17 @@ impl Pimlico { let gas_changed = estimate.apply(&mut op)?; let required = terms.maximum_token_cost(&op)?; if gas_changed || required > allowance { - allowance = allowance.max(approval_margin(required)?); + allowance = allowance.max(with_margin(required, 5)?); continue; } - if terms.valid_until == 0 || terms.valid_until > timestamp.saturating_add(900) { + if terms.valid_until == 0 + || terms.valid_until > timestamp.saturating_add(MAX_PAYMASTER_VALIDITY_SECONDS) + { return Err(UsdtError::InvalidResponse); } - if terms.valid_after > timestamp || terms.valid_until <= timestamp.saturating_add(15) { + if terms.valid_after > timestamp + || terms.valid_until <= timestamp.saturating_add(MIN_PAYMASTER_VALIDITY_SECONDS) + { return Err(UsdtError::QuoteExpired); } return Ok(( @@ -278,13 +282,6 @@ fn with_approval(calls: &[(Address, Bytes)], amount: U256) -> Bytes { super::account::batch(&batch) } -fn approval_margin(value: U256) -> Result { - value - .checked_add(value / U256::from(20)) - .and_then(|value| value.checked_add(U256::from(1))) - .ok_or(UsdtError::InvalidResponse) -} - struct Terms { exchange_rate: U256, post_op_gas: U256, diff --git a/src/modules/usdt/rpc.rs b/src/modules/usdt/rpc.rs index 5051048..1762cc1 100644 --- a/src/modules/usdt/rpc.rs +++ b/src/modules/usdt/rpc.rs @@ -16,6 +16,14 @@ pub(super) struct Block { pub transactions: Vec, } +impl Block { + pub fn timestamp(&self) -> Result { + self.timestamp + .try_into() + .map_err(|_| UsdtError::InvalidResponse) + } +} + pub(super) struct Rpc { client: reqwest::Client, url: String, @@ -191,10 +199,19 @@ impl Rpc { } pub async fn contract(&self, to: Address, call: C) -> Result { + self.contract_at(to, call, "latest").await + } + + pub async fn contract_at( + &self, + to: Address, + call: C, + block: &str, + ) -> Result { let bytes: Bytes = self .call( "eth_call", - json!([{"to":to,"data":Bytes::from(call.abi_encode())},"latest"]), + json!([{"to":to,"data":Bytes::from(call.abi_encode())},block]), ) .await?; C::abi_decode_returns(&bytes).map_err(|_| UsdtError::InvalidResponse) diff --git a/src/modules/usdt/store.rs b/src/modules/usdt/store.rs index 91f6214..d361566 100644 --- a/src/modules/usdt/store.rs +++ b/src/modules/usdt/store.rs @@ -1,4 +1,7 @@ -use super::{transaction::Plan, UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus}; +use super::{ + history::HISTORY_REVISIT_BLOCKS, transaction::Plan, UsdtError, UsdtQuote, UsdtTransfer, + UsdtTransferStatus, +}; use rusqlite::{params, Connection, OptionalExtension, Transaction}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{ @@ -27,11 +30,12 @@ impl Store { CREATE TABLE IF NOT EXISTS usdt_identity (id INTEGER PRIMARY KEY CHECK(id=1), identity TEXT NOT NULL); CREATE TABLE IF NOT EXISTS usdt_sync (id INTEGER PRIMARY KEY CHECK(id=1), newest INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_history_progress (id INTEGER PRIMARY KEY CHECK(id=1), next INTEGER NOT NULL); - CREATE TABLE IF NOT EXISTS usdt_history_receipts (hash TEXT PRIMARY KEY); + CREATE TABLE IF NOT EXISTS usdt_history_receipts (hash TEXT PRIMARY KEY, block_number INTEGER NOT NULL, block_hash TEXT NOT NULL, complete_receipt INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_history_blocks (number INTEGER PRIMARY KEY, hash TEXT NOT NULL, complete INTEGER NOT NULL); CREATE TABLE IF NOT EXISTS usdt_quotes (id TEXT PRIMARY KEY, data TEXT NOT NULL); CREATE TABLE IF NOT EXISTS usdt_nonce_recovery (id TEXT PRIMARY KEY, block_hash TEXT NOT NULL, next_transaction INTEGER NOT NULL); - CREATE TABLE IF NOT EXISTS usdt_transfers (id TEXT PRIMARY KEY, hash TEXT NOT NULL, raw TEXT, data TEXT NOT NULL);")?; + CREATE TABLE IF NOT EXISTS usdt_transfers (id TEXT PRIMARY KEY, hash TEXT NOT NULL, raw TEXT, data TEXT NOT NULL); + CREATE INDEX IF NOT EXISTS usdt_transfers_hash ON usdt_transfers(hash);")?; connection.execute( "INSERT OR IGNORE INTO usdt_identity VALUES (1,?1)", [identity], @@ -88,15 +92,7 @@ impl Store { } pub fn transfer_by_hash(&self, hash: &str) -> Result, UsdtError> { - let data: Option = self - .connection()? - .query_row( - "SELECT data FROM usdt_transfers WHERE hash=?1", - [hash], - |r| r.get(0), - ) - .optional()?; - data.map(|data| decode(&data)).transpose() + find_transfer_by_hash(&*self.connection()?, hash) } pub fn transfers(&self) -> Result, UsdtError> { @@ -131,22 +127,9 @@ impl Store { } pub fn update_transfer(&self, transfer: &UsdtTransfer) -> Result<(), UsdtError> { - let settled = matches!( - transfer.status, - UsdtTransferStatus::Confirmed - | UsdtTransferStatus::Failed - | UsdtTransferStatus::Replaced - ); let mut connection = self.connection()?; let tx = connection.transaction()?; - tx.execute("UPDATE usdt_transfers SET data=?1, raw=CASE WHEN ?2 THEN NULL ELSE raw END WHERE id=?3", - params![serde_json::to_string(transfer)?, settled, transfer.id])?; - if settled { - tx.execute( - "DELETE FROM usdt_nonce_recovery WHERE id=?1", - [&transfer.id], - )?; - } + write_transfer(&tx, transfer)?; tx.commit()?; Ok(()) } @@ -189,10 +172,13 @@ impl Store { let mut connection = self.connection()?; let tx = connection.transaction()?; tx.execute("DELETE FROM usdt_history_progress", [])?; - tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.execute( + "DELETE FROM usdt_history_receipts WHERE complete_receipt=0 OR block_number < ?1", + [newest.saturating_sub(HISTORY_REVISIT_BLOCKS)], + )?; tx.execute( "DELETE FROM usdt_history_blocks WHERE number < ?1", - [newest.saturating_sub(4096)], + [newest.saturating_sub(HISTORY_REVISIT_BLOCKS)], )?; tx.execute("INSERT INTO usdt_sync (id,newest) VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET newest=excluded.newest", [newest])?; tx.commit()?; @@ -215,10 +201,13 @@ impl Store { let tx = connection.transaction()?; tx.execute( "DELETE FROM usdt_history_blocks WHERE number < ?1", - [next.saturating_sub(4096)], + [next.saturating_sub(HISTORY_REVISIT_BLOCKS)], )?; tx.execute("INSERT INTO usdt_history_progress VALUES (1,?1) ON CONFLICT(id) DO UPDATE SET next=excluded.next", [next])?; - tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.execute( + "DELETE FROM usdt_history_receipts WHERE complete_receipt=0 OR block_number < ?1", + [next.saturating_sub(HISTORY_REVISIT_BLOCKS)], + )?; tx.commit()?; Ok(()) } @@ -237,7 +226,10 @@ impl Store { if saved_hash == hash { return Ok(complete); } - tx.execute("DELETE FROM usdt_history_receipts", [])?; + tx.execute( + "DELETE FROM usdt_history_receipts WHERE block_number=?1", + [number], + )?; } tx.execute("INSERT INTO usdt_history_blocks VALUES (?1,?2,0) ON CONFLICT(number) DO UPDATE SET hash=excluded.hash,complete=0", params![number, hash])?; tx.commit()?; @@ -252,10 +244,15 @@ impl Store { Ok(()) } - pub fn has_history_receipt(&self, hash: &str) -> Result { + pub fn has_history_receipt( + &self, + hash: &str, + block_hash: &str, + require_complete: bool, + ) -> Result { Ok(self.connection()?.query_row( - "SELECT EXISTS(SELECT 1 FROM usdt_history_receipts WHERE hash=?1)", - [hash], + "SELECT EXISTS(SELECT 1 FROM usdt_history_receipts WHERE hash=?1 AND block_hash=?2 AND (complete_receipt=1 OR ?3=0))", + params![hash, block_hash, require_complete], |row| row.get(0), )?) } @@ -264,13 +261,16 @@ impl Store { &self, transfers: &[UsdtTransfer], hash: &str, + block_number: u64, + block_hash: &str, + complete_receipt: bool, ) -> Result<(), UsdtError> { let mut connection = self.connection()?; let tx = connection.transaction()?; Self::merge_history(&tx, transfers)?; tx.execute( - "INSERT OR IGNORE INTO usdt_history_receipts VALUES (?1)", - [hash], + "INSERT INTO usdt_history_receipts VALUES (?1,?2,?3,?4) ON CONFLICT(hash) DO UPDATE SET block_number=excluded.block_number,block_hash=excluded.block_hash,complete_receipt=excluded.complete_receipt", + params![hash, block_number, block_hash, complete_receipt], )?; tx.commit()?; Ok(()) @@ -282,25 +282,11 @@ impl Store { .user_operation_hash .as_deref() .unwrap_or(&transfer.id); - let existing: Option = tx - .query_row( - "SELECT data FROM usdt_transfers WHERE hash=?1", - [hash], - |row| row.get(0), - ) - .optional()?; + let existing = find_transfer_by_hash(tx, hash)?; let mut transfer = transfer.clone(); - if let Some(data) = existing { - let saved: UsdtTransfer = decode(&data)?; + if let Some(saved) = existing { transfer.id = saved.id; - tx.execute( - "UPDATE usdt_transfers SET data=?1, raw=NULL WHERE id=?2", - params![serde_json::to_string(&transfer)?, transfer.id], - )?; - tx.execute( - "DELETE FROM usdt_nonce_recovery WHERE id=?1", - [&transfer.id], - )?; + write_transfer(tx, &transfer)?; } else { tx.execute( "INSERT INTO usdt_transfers (id,hash,data) VALUES (?1,?2,?3)", @@ -311,18 +297,18 @@ impl Store { Ok(()) } - pub fn unsettled(&self) -> Result, UsdtError> { - let connection = self.connection()?; - let mut statement = connection.prepare("SELECT data FROM usdt_transfers")?; - let rows = statement.query_map([], |row| row.get::<_, String>(0))?; - let mut result = Vec::new(); - for row in rows { - let transfer: UsdtTransfer = decode(&row?)?; - if transfer.status == UsdtTransferStatus::Pending { - result.push(transfer); - } - } - Ok(result) + pub fn pending_operation(&self) -> Result, UsdtError> { + let saved: Option<(String, String)> = self + .connection()? + .query_row( + "SELECT data,raw FROM usdt_transfers WHERE raw IS NOT NULL", + [], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional()?; + saved + .map(|(data, raw)| Ok((decode(&data)?, decode(&raw)?))) + .transpose() } pub fn pending_plan(&self, id: &str) -> Result, UsdtError> { @@ -337,6 +323,35 @@ impl Store { } } +fn write_transfer(connection: &Connection, transfer: &UsdtTransfer) -> Result<(), UsdtError> { + let settled = transfer.status != UsdtTransferStatus::Pending; + connection.execute( + "UPDATE usdt_transfers SET data=?1, raw=CASE WHEN ?2 THEN NULL ELSE raw END WHERE id=?3", + params![serde_json::to_string(transfer)?, settled, transfer.id], + )?; + if settled { + connection.execute( + "DELETE FROM usdt_nonce_recovery WHERE id=?1", + [&transfer.id], + )?; + } + Ok(()) +} + +fn find_transfer_by_hash( + connection: &Connection, + hash: &str, +) -> Result, UsdtError> { + let data: Option = connection + .query_row( + "SELECT data FROM usdt_transfers WHERE hash=?1", + [hash], + |row| row.get(0), + ) + .optional()?; + data.map(|data| decode(&data)).transpose() +} + fn decode(data: &str) -> Result { serde_json::from_str(data).map_err(|error| UsdtError::Storage { reason: error.to_string(), diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index 7ce6b39..d016869 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -242,6 +242,8 @@ struct ChainState { mined: bool, tip: u64, timestamp: alloy_primitives::U256, + block_hashes: std::collections::BTreeMap, + block_timestamps: std::collections::BTreeMap, reject_broadcast: bool, delay_gas_estimate: bool, paymaster: alloy_primitives::Address, @@ -250,6 +252,7 @@ struct ChainState { receipt_logs: Option>, incoming: bool, hide_logs: bool, + hide_operation_logs: bool, hide_receipts: bool, receipt_failure: Option, oversized_block: Option, @@ -261,6 +264,7 @@ struct ChainState { max_log_range: Option, oversized_logs: bool, log_requests: usize, + log_ranges: Vec<(u64, u64)>, incoming_count: u64, block_reads: usize, fail_block_read_at: Option, @@ -293,6 +297,8 @@ impl MockChain { mined: false, tip: 20000, timestamp: U256::from(wallet::now()), + block_hashes: Default::default(), + block_timestamps: Default::default(), reject_broadcast: false, delay_gas_estimate: false, paymaster: paymaster::PAYMASTER, @@ -301,6 +307,7 @@ impl MockChain { receipt_logs: None, incoming: false, hide_logs: false, + hide_operation_logs: false, hide_receipts: false, receipt_failure: None, oversized_block: None, @@ -312,6 +319,7 @@ impl MockChain { max_log_range: None, oversized_logs: false, log_requests: 0, + log_ranges: vec![], incoming_count: 0, block_reads: 0, fail_block_read_at: None, @@ -391,8 +399,12 @@ impl MockChain { if body["method"] == "eth_getLogs" { if let Some(logs) = response["result"].as_array_mut() { for log in logs { + let number = serde_json::from_value::(log["blockNumber"].clone()) + .ok() + .and_then(|number| u64::try_from(number).ok()) + .unwrap_or(20000); log.as_object_mut().unwrap().entry("blockHash").or_insert( - serde_json::json!(alloy_primitives::B256::repeat_byte(9)), + serde_json::json!(server_state.lock().unwrap().block_hash(number)), ); } } @@ -418,6 +430,12 @@ impl MockChain { } } impl ChainState { + fn block_hash(&self, number: u64) -> alloy_primitives::B256 { + self.block_hashes.get(&number).copied().unwrap_or_else(|| { + alloy_primitives::B256::from(alloy_primitives::U256::from(number).to_be_bytes::<32>()) + }) + } + fn respond(&mut self, body: &serde_json::Value) -> serde_json::Value { use alloy_primitives::{Bytes, U256}; use alloy_sol_types::{SolCall, SolValue}; @@ -449,7 +467,17 @@ impl ChainState { self.fail_block_read_at = None; return json!({"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"temporarily unavailable"}}); } - json!({"hash":alloy_primitives::B256::repeat_byte(9),"timestamp":self.timestamp,"transactions":self.block_transactions.clone().unwrap_or_else(|| vec![alloy_primitives::B256::repeat_byte(7)])}) + let number = u64::try_from( + serde_json::from_value::(body["params"][0].clone()).unwrap(), + ) + .unwrap(); + let timestamp = self + .block_timestamps + .range(..=number) + .next_back() + .map(|(_, timestamp)| *timestamp) + .unwrap_or(self.timestamp); + json!({"hash":self.block_hash(number),"timestamp":timestamp,"transactions":self.block_transactions.clone().unwrap_or_else(|| vec![alloy_primitives::B256::repeat_byte(7)])}) } "eth_getCode" => json!(self.account_code), "eth_getTransactionCount" => json!(U256::from(self.authorization_nonce)), @@ -536,6 +564,8 @@ impl ChainState { let filter = &body["params"][0]; let from: U256 = serde_json::from_value(filter["fromBlock"].clone()).unwrap(); let to: U256 = serde_json::from_value(filter["toBlock"].clone()).unwrap(); + self.log_ranges + .push((u64::try_from(from).unwrap(), u64::try_from(to).unwrap())); if self .oversized_block .is_some_and(|block| from <= U256::from(block) && to >= U256::from(block)) @@ -602,6 +632,7 @@ impl ChainState { } if self.mined && !self.hide_logs + && !self.hide_operation_logs && from <= U256::from(20000) && to >= U256::from(20000) && (filter["address"] == json!(account::ENTRY_POINT) @@ -628,7 +659,14 @@ impl ChainState { { return json!({"jsonrpc":"2.0","id":1,"result":null}); } - json!({"transactionHash":body["params"][0],"blockHash":alloy_primitives::B256::repeat_byte(9),"blockNumber":"0x4e20","logs":self.receipt_logs.clone().unwrap_or_else(|| self.event_logs()),"padding":" ".repeat(self.receipt_padding)}) + let logs = self.receipt_logs.clone().unwrap_or_else(|| { + if body["params"][0] == json!(alloy_primitives::B256::repeat_byte(7)) { + self.event_logs() + } else { + vec![] + } + }); + json!({"transactionHash":body["params"][0],"blockHash":self.block_hash(20000),"blockNumber":"0x4e20","logs":logs,"padding":" ".repeat(self.receipt_padding)}) } "eth_getTransactionByHash" => { let op = &self.operations[0]; @@ -735,13 +773,10 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); - let quote = tokio::time::timeout( - std::time::Duration::from_secs(3), - wallet.quote_transfer(RECIPIENT.into(), 1_000_000), - ) - .await - .expect("An idle wallet must quote without a fixed per-request delay") - .unwrap(); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); let next = wallet .quote_transfer(RECIPIENT.into(), 2_000_000) .await @@ -752,7 +787,7 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { .await .unwrap(); assert_eq!(sent.status, UsdtTransferStatus::Pending); - assert!(sent.tx_hash.is_empty()); + assert!(sent.tx_hash.is_none()); let op = chain.state.lock().unwrap().operations[0].clone(); assert_eq!(op.sender.to_checksum(None), wallet.receive_address()); assert_eq!( @@ -803,7 +838,7 @@ async fn signed_operation_survives_uncertain_broadcast_and_restart() { assert_eq!(history.len(), 1); assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); assert_eq!(history[0].fee, Some(123)); - assert!(!history[0].tx_hash.is_empty()); + assert!(history[0].tx_hash.is_some()); } #[tokio::test] @@ -910,7 +945,10 @@ async fn wrong_network_owner_nonce_balance_and_paymaster_cannot_sign() { #[tokio::test] async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { - for nonce in [0, 1] { + for (nonce, log_error) in [ + (0, None), + (1, Some((-32002, "Provider unavailable".into()))), + ] { let chain = MockChain::start().await; chain.state.lock().unwrap().nonce = nonce; let dir = tempfile::tempdir().unwrap(); @@ -929,6 +967,7 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { state.timestamp += alloy_primitives::U256::from(180); state.tip += 3; state.max_log_range = Some(1); + state.log_error = log_error.clone(); } assert_eq!( wallet.refresh_transfers().await.unwrap()[0].status, @@ -938,7 +977,20 @@ async fn expired_unmined_operation_releases_nonce_for_a_new_approval() { wallet.quote_transfer(RECIPIENT.into(), 1_000_000).await, Err(UsdtError::PendingTransfer) )); - chain.state.lock().unwrap().timestamp += alloy_primitives::U256::from(421); + { + let mut state = chain.state.lock().unwrap(); + state.timestamp += alloy_primitives::U256::from(421); + state.log_error = Some((-32016, "Provider rate limit exceeded".into())); + } + assert!(matches!( + wallet.refresh_transfers().await, + Err(UsdtError::RateLimited) + )); + assert_eq!( + wallet.history().unwrap()[0].status, + UsdtTransferStatus::Pending + ); + chain.state.lock().unwrap().log_error = log_error; assert_eq!( wallet.refresh_transfers().await.unwrap()[0].status, UsdtTransferStatus::Failed @@ -1241,7 +1293,7 @@ async fn wrapped_history_preserves_signed_payments_and_restores_token_transfers( assert_eq!(payment.recipient, RECIPIENT); assert_eq!(payment.status, UsdtTransferStatus::Confirmed); assert_eq!(payment.fee, Some(123)); - assert!(!payment.tx_hash.is_empty()); + assert!(payment.tx_hash.is_some()); assert!(reopened.store.pending_plan(&sent.id).unwrap().is_none()); let restored_dir = tempfile::tempdir().unwrap(); @@ -1275,7 +1327,7 @@ fn stored_activity_is_complete_and_sorted_newest_first() { let transfers: Vec<_> = (0..501) .map(|index| UsdtTransfer { id: format!("receipt-{index}"), - tx_hash: format!("tx-{index}"), + tx_hash: Some(format!("tx-{index}")), user_operation_hash: None, recipient: RECIPIENT.into(), amount: 1, @@ -1284,10 +1336,11 @@ fn stored_activity_is_complete_and_sorted_newest_first() { is_incoming: true, status: UsdtTransferStatus::Confirmed, timestamp: index, - explorer_url: String::new(), }) .collect(); - store.save_history_receipt(&transfers, "receipt").unwrap(); + store + .save_history_receipt(&transfers, "receipt", 1000, "block", true) + .unwrap(); store.complete_history(1000).unwrap(); let history = store.transfers().unwrap(); assert_eq!(history.len(), 501); @@ -1361,9 +1414,10 @@ async fn interrupted_history_resumes_without_repeating_completed_work() { .send(quote.id, TEST_PHRASE.into(), None) .await .unwrap(); + let incoming_count = 100; { let mut state = chain.state.lock().unwrap(); - state.incoming_count = 100; + state.incoming_count = incoming_count as u64; state.tip = 508_000_000; state.block_reads = 0; state.fail_block_read_at = Some(25); @@ -1388,16 +1442,20 @@ async fn interrupted_history_resumes_without_repeating_completed_work() { drop(restored); let restored = chain.wallet(&restored_dir); sync_history_to_tip(&restored).await; - assert_eq!(restored.history().unwrap().len(), 100); - assert_eq!(chain.state.lock().unwrap().block_reads, 101); + assert_eq!(restored.history().unwrap().len(), incoming_count); + assert_eq!(chain.state.lock().unwrap().block_reads, incoming_count + 1); sync_history_to_tip(&restored).await; - assert_eq!(restored.history().unwrap().len(), 100); - assert_eq!(chain.state.lock().unwrap().block_reads, 101); + assert_eq!(restored.history().unwrap().len(), incoming_count); + assert_eq!(chain.state.lock().unwrap().block_reads, incoming_count + 1); assert!(restored.store.history_progress().unwrap().is_none()); assert_eq!(restored.store.synced_block().unwrap().unwrap(), 507_999_998); assert!(!restored .store - .has_history_receipt(&restored.history().unwrap()[0].tx_hash) + .has_history_receipt( + restored.history().unwrap()[0].tx_hash.as_deref().unwrap(), + &format!("{:#x}", chain.state.lock().unwrap().block_hash(20100)), + false + ) .unwrap()); } @@ -1416,10 +1474,13 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { .await .unwrap(); drop(wallet); + let expiry_block = 22_500; { let mut state = chain.state.lock().unwrap(); state.nonce = 1; state.timestamp += alloy_primitives::U256::from(600); + let expired = state.timestamp + alloy_primitives::U256::from(1); + state.block_timestamps.insert(expiry_block, expired); state.tip = 508_000_000; state.replacement_block = Some(507_000_000); state.hide_logs = true; @@ -1428,6 +1489,11 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { } let wallet = chain.wallet(&dir); assert!(wallet.refresh_transfers().await.is_err()); + let log_ranges = chain.state.lock().unwrap().log_ranges.clone(); + assert!(!log_ranges.is_empty()); + assert!(log_ranges + .iter() + .all(|&(start, end)| start == end || end <= expiry_block)); assert_eq!( wallet.history().unwrap()[0].status, UsdtTransferStatus::Pending @@ -1440,7 +1506,7 @@ async fn replacement_after_expiry_recovers_pending_send_after_restart() { state.hide_receipts = false; state.receipt_response = Some(serde_json::json!({ "transactionHash":alloy_primitives::B256::repeat_byte(7), - "blockHash":alloy_primitives::B256::repeat_byte(9), + "blockHash":state.block_hash(507_000_000), "blockNumber":alloy_primitives::U256::from(507_000_000),"logs":[] })); } @@ -1525,14 +1591,25 @@ async fn history_distinguishes_rate_limits_from_log_range_limits() { #[tokio::test] async fn history_budget_returns_incomplete_and_resumes_to_tip() { let chain = MockChain::start().await; - chain.state.lock().unwrap().tip = 508_000_000; + { + let mut state = chain.state.lock().unwrap(); + state.tip = 508_000_000; + state.max_log_range = Some(1_000_000); + } let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); assert!(!wallet.sync_history().await.unwrap()); let next = wallet.store.history_progress().unwrap().unwrap(); assert!(next > 0 && next < 508_000_000); + assert!( + wallet + .history_range_limit + .load(std::sync::atomic::Ordering::Relaxed) + <= 1_000_000 + ); + chain.state.lock().unwrap().tip = next + 1_000_000; sync_history_to_tip(&wallet).await; - assert_eq!(wallet.store.synced_block().unwrap(), Some(507_999_998)); + assert_eq!(wallet.store.synced_block().unwrap(), Some(next + 999_998)); } #[tokio::test] @@ -1802,21 +1879,30 @@ async fn seed_restore_includes_external_token_sends_without_duplicate_operation_ } #[tokio::test] -async fn gas_price_changes_require_a_new_quote_before_signing() { - let chain = MockChain::start().await; - let dir = tempfile::tempdir().unwrap(); - let wallet = chain.wallet(&dir); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000) - .await - .unwrap(); - chain.state.lock().unwrap().gas_price = 60_000_000; - assert!(matches!( - wallet.send(quote.id, TEST_PHRASE.into(), None).await, - Err(UsdtError::QuoteExpired) - )); - assert!(wallet.history().unwrap().is_empty()); - assert!(chain.state.lock().unwrap().operations.is_empty()); +async fn gas_price_changes_respect_the_approved_fee() { + for (gas_price, accepted) in [(52_000_000, true), (60_000_000, false)] { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let plan = wallet.store.quote("e.id).unwrap().plan; + chain.state.lock().unwrap().gas_price = gas_price; + let result = wallet.send(quote.id, TEST_PHRASE.into(), None).await; + if accepted { + result.unwrap(); + assert_eq!( + chain.state.lock().unwrap().operations[0].max_fee_per_gas, + plan.operation.max_fee_per_gas + ); + } else { + assert!(matches!(result, Err(UsdtError::QuoteExpired))); + assert!(wallet.history().unwrap().is_empty()); + assert!(chain.state.lock().unwrap().operations.is_empty()); + } + } } #[tokio::test] @@ -1843,7 +1929,7 @@ async fn consumed_nonce_recovery_requires_complete_receipts_and_resumes_after_re } assert!(wallet.refresh_transfers().await.is_err()); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); - let block_hash = format!("{:#x}", B256::repeat_byte(9)); + let block_hash = format!("{:#x}", chain.state.lock().unwrap().block_hash(20000)); assert_eq!( wallet.store.nonce_recovery(&sent.id, &block_hash).unwrap(), 1 @@ -1871,6 +1957,40 @@ async fn consumed_nonce_recovery_requires_complete_receipts_and_resumes_after_re #[tokio::test] async fn consuming_block_receipts_recover_a_payment_hidden_from_log_queries() { + for unavailable in [false, true] { + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 1; + state.tip += 3; + state.hide_operation_logs = true; + state.replacement_block = Some(20000); + if unavailable { + state.log_error = Some((-32002, "Provider unavailable".into())); + } + state.mined = true; + } + let history = wallet.refresh_transfers().await.unwrap(); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); + } +} + +#[tokio::test] +async fn consumed_nonce_recovery_preserves_malformed_operation_evidence() { + use serde_json::json; let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); let wallet = chain.wallet(&dir); @@ -1887,13 +2007,36 @@ async fn consuming_block_receipts_recover_a_payment_hidden_from_log_queries() { state.nonce = 1; state.tip += 3; state.hide_logs = true; + state.replacement_block = Some(20000); state.mined = true; + let mut logs = state.event_logs(); + logs.iter_mut() + .find(|log| log["address"] == json!(account::ENTRY_POINT.to_checksum(None))) + .unwrap()["data"] = json!("0x"); + state.receipt_logs = Some(logs); } - let history = wallet.refresh_transfers().await.unwrap(); + assert!(matches!( + wallet.refresh_transfers().await, + Err(UsdtError::InvalidResponse) + )); + assert_eq!( + wallet.history().unwrap()[0].status, + UsdtTransferStatus::Pending + ); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + drop(wallet); + { + let mut state = chain.state.lock().unwrap(); + let mut logs = state.event_logs(); + logs.insert(0, json!({"address":account::ENTRY_POINT,"topics":[alloy_primitives::keccak256("BeforeExecution()")],"data":"0x"})); + state.receipt_logs = Some(logs); + } + let restored = chain.wallet(&dir); + let history = restored.refresh_transfers().await.unwrap(); assert_eq!(history[0].id, sent.id); assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); assert_eq!(history[0].fee, Some(123)); - assert!(wallet.store.pending_plan(&sent.id).unwrap().is_none()); + assert!(restored.store.pending_plan(&sent.id).unwrap().is_none()); } #[tokio::test] @@ -1937,6 +2080,22 @@ async fn dense_block_history_recovers_large_receipts_without_skipping_after_rest let restored = chain.wallet(&dir); sync_history_to_tip(&restored).await; assert_eq!(chain.state.lock().unwrap().receipt_reads, reads); + drop(restored); + { + let mut state = chain.state.lock().unwrap(); + state + .block_hashes + .insert(20000, alloy_primitives::B256::repeat_byte(0xaa)); + } + let restored = chain.wallet(&dir); + restored.store.save_history_progress(20000).unwrap(); + sync_history_to_tip(&restored).await; + assert_eq!(chain.state.lock().unwrap().receipt_reads, reads + 1); + assert_eq!(restored.history().unwrap()[0].id, sent.id); + assert_eq!( + restored.history().unwrap()[0].status, + UsdtTransferStatus::Confirmed + ); } #[tokio::test] @@ -2000,27 +2159,6 @@ async fn first_submission_precheck_releases_an_operation_that_was_never_sent() { .unwrap(); } -#[tokio::test] -async fn moderate_gas_price_movement_preserves_the_approved_fee() { - let chain = MockChain::start().await; - let dir = tempfile::tempdir().unwrap(); - let wallet = chain.wallet(&dir); - let quote = wallet - .quote_transfer(RECIPIENT.into(), 1_000_000) - .await - .unwrap(); - let plan = wallet.store.quote("e.id).unwrap().plan; - chain.state.lock().unwrap().gas_price = 52_000_000; - wallet - .send(quote.id, TEST_PHRASE.into(), None) - .await - .unwrap(); - assert_eq!( - chain.state.lock().unwrap().operations[0].max_fee_per_gas, - plan.operation.max_fee_per_gas - ); -} - #[tokio::test] async fn unknown_paymaster_history_preserves_principal_fee_and_refund() { use alloy_primitives::{Address, B256, U256}; @@ -2131,7 +2269,7 @@ async fn settlement_requires_matching_canonical_receipts() { invalid[field] = value; chain.state.lock().unwrap().receipt_response = Some(invalid); assert!(matches!( - wallet.refresh_transfer(sent.id.clone()).await, + wallet.check_recent_execution(sent.id.clone()).await, Err(UsdtError::InvalidResponse) )); assert!(matches!( @@ -2339,7 +2477,7 @@ async fn recent_execution_requires_the_expected_token_transfer() { logs[0]["data"] = json!(token.data); chain.state.lock().unwrap().receipt_logs = Some(logs); assert!(matches!( - wallet.refresh_transfer(sent.id.clone()).await, + wallet.check_recent_execution(sent.id.clone()).await, Err(UsdtError::InvalidResponse) )); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); @@ -2348,13 +2486,13 @@ async fn recent_execution_requires_the_expected_token_transfer() { logs.remove(0); chain.state.lock().unwrap().receipt_logs = Some(logs); assert!(matches!( - wallet.refresh_transfer(sent.id.clone()).await, + wallet.check_recent_execution(sent.id.clone()).await, Err(UsdtError::InvalidResponse) )); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); chain.state.lock().unwrap().receipt_logs = None; let result = wallet - .refresh_transfer(sent.id.clone()) + .check_recent_execution(sent.id.clone()) .await .unwrap() .unwrap(); @@ -2382,7 +2520,7 @@ async fn execution_check_preserves_unmined_payments_and_throttling() { state.timestamp += alloy_primitives::U256::from(1000); } let result = wallet - .refresh_transfer(sent.id.clone()) + .check_recent_execution(sent.id.clone()) .await .unwrap() .unwrap(); @@ -2391,8 +2529,147 @@ async fn execution_check_preserves_unmined_payments_and_throttling() { assert_eq!(chain.state.lock().unwrap().operations.len(), 1); chain.state.lock().unwrap().log_error = Some((-32016, "rate limit".into())); assert!(matches!( - wallet.refresh_transfer(sent.id.clone()).await, + wallet.check_recent_execution(sent.id.clone()).await, Err(UsdtError::RateLimited) )); assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); } + +#[tokio::test] +async fn interrupted_nonce_recovery_restarts_on_a_changed_block() { + use alloy_primitives::B256; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.nonce = 1; + state.tip += 3; + state.hide_logs = true; + state.block_transactions = Some(vec![B256::repeat_byte(6), B256::repeat_byte(7)]); + state.receipt_failure = Some(B256::repeat_byte(7)); + } + assert!(wallet.refresh_transfers().await.is_err()); + assert!(wallet.store.pending_plan(&sent.id).unwrap().is_some()); + drop(wallet); + { + let mut state = chain.state.lock().unwrap(); + state.block_hashes.insert(20000, B256::repeat_byte(0xaa)); + state.block_transactions = Some(vec![B256::repeat_byte(7), B256::repeat_byte(6)]); + state.receipt_failure = None; + state.mined = true; + } + let restored = chain.wallet(&dir); + let history = restored.refresh_transfers().await.unwrap(); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].status, UsdtTransferStatus::Confirmed); + assert_eq!(history[0].fee, Some(123)); + assert!(restored.store.pending_plan(&sent.id).unwrap().is_none()); +} + +#[tokio::test] +async fn history_reuses_receipts_only_while_their_block_remains_canonical() { + use alloy_primitives::{B256, U256}; + use alloy_sol_types::SolEvent; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + let sent = wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.tip += 3; + } + sync_history_to_tip(&wallet).await; + let reads = chain.state.lock().unwrap().receipt_reads; + drop(wallet); + let restored = chain.wallet(&dir); + sync_history_to_tip(&restored).await; + assert_eq!(chain.state.lock().unwrap().receipt_reads, reads); + { + let mut state = chain.state.lock().unwrap(); + let mut logs = state.event_logs(); + let mut event = transaction::Paymaster::UserOperationSponsored::decode_log_data( + &transaction::event_data(&logs[1]).unwrap(), + ) + .unwrap(); + event.tokenAmountPaid = U256::from(456); + logs[1]["data"] = json!(event.encode_log_data().data); + state.receipt_logs = Some(logs); + let mut stale = state.event_logs()[2].clone(); + stale["blockHash"] = json!(state.block_hash(20000)); + state.log_response = Some(vec![stale]); + state.block_hashes.insert(20000, B256::repeat_byte(0xaa)); + } + assert!(matches!( + restored.sync_history().await, + Err(UsdtError::NetworkUnavailable) + )); + assert_eq!(restored.history().unwrap()[0].fee, Some(123)); + chain.state.lock().unwrap().log_response = None; + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history[0].id, sent.id); + assert_eq!(history[0].fee, Some(456)); + assert_eq!(chain.state.lock().unwrap().receipt_reads, reads + 1); +} + +#[tokio::test] +async fn incoming_log_progress_does_not_hide_later_receipt_evidence() { + use alloy_primitives::{B256, U256}; + use serde_json::json; + let chain = MockChain::start().await; + let dir = tempfile::tempdir().unwrap(); + let wallet = chain.wallet(&dir); + let quote = wallet + .quote_transfer(RECIPIENT.into(), 1_000_000) + .await + .unwrap(); + wallet + .send(quote.id, TEST_PHRASE.into(), None) + .await + .unwrap(); + { + let mut state = chain.state.lock().unwrap(); + state.mined = true; + state.incoming = true; + state.tip += 3; + let mut incoming = state.event_logs().last().unwrap().clone(); + incoming["transactionHash"] = json!(B256::repeat_byte(7)); + incoming["blockNumber"] = json!(U256::from(20000)); + state.log_response = Some(vec![incoming]); + } + let restore_dir = tempfile::tempdir().unwrap(); + let restored = chain.wallet(&restore_dir); + sync_history_to_tip(&restored).await; + assert_eq!(restored.history().unwrap().len(), 1); + assert!(restored.history().unwrap()[0].is_incoming); + assert_eq!(chain.state.lock().unwrap().receipt_reads, 0); + chain.state.lock().unwrap().log_response = None; + sync_history_to_tip(&restored).await; + let history = restored.history().unwrap(); + assert_eq!(history.len(), 2); + let payment = history + .iter() + .find(|transfer| !transfer.is_incoming) + .unwrap(); + assert_eq!(payment.amount, 1_000_000); + assert_eq!(payment.fee, Some(123)); + assert_eq!(chain.state.lock().unwrap().receipt_reads, 1); +} diff --git a/src/modules/usdt/transaction.rs b/src/modules/usdt/transaction.rs index 6b882b8..8b4d3d6 100644 --- a/src/modules/usdt/transaction.rs +++ b/src/modules/usdt/transaction.rs @@ -32,21 +32,33 @@ sol! { } } +pub(super) fn entry_point_event( + log: &serde_json::Value, +) -> Result, UsdtError> { + use alloy_sol_types::SolEvent; + let address: alloy_primitives::Address = serde_json::from_value(log["address"].clone())?; + if address != super::account::ENTRY_POINT { + return Ok(None); + } + let data = event_data(log)?; + if data.topics().first() != Some(&EntryPoint::UserOperationEvent::SIGNATURE_HASH) { + return Ok(None); + } + EntryPoint::UserOperationEvent::decode_log_data(&data) + .map(Some) + .map_err(|_| UsdtError::InvalidResponse) +} + pub(super) fn operation_logs( receipt: &serde_json::Value, hash: B256, ) -> Result<&[serde_json::Value], UsdtError> { - use alloy_sol_types::SolEvent; let logs = receipt["logs"] .as_array() .ok_or(UsdtError::InvalidResponse)?; let mut start = 0; for (index, log) in logs.iter().enumerate() { - let address: alloy_primitives::Address = serde_json::from_value(log["address"].clone())?; - if address != super::account::ENTRY_POINT { - continue; - } - if let Ok(event) = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) { + if let Some(event) = entry_point_event(log)? { if event.userOpHash == hash { return Ok(&logs[start..=index]); } diff --git a/src/modules/usdt/types.rs b/src/modules/usdt/types.rs index 16453c7..391dfc6 100644 --- a/src/modules/usdt/types.rs +++ b/src/modules/usdt/types.rs @@ -3,7 +3,6 @@ use serde::{Deserialize, Serialize}; pub(super) const CHAIN_ID: u64 = 42161; pub(super) const TOKEN: Address = address!("Fd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"); -pub(super) const EXPLORER: &str = "https://arbiscan.io"; #[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)] pub struct UsdtPaymentRequest { @@ -24,16 +23,21 @@ pub struct UsdtQuote { #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)] pub enum UsdtTransferStatus { + /// Signed payment awaiting a conclusive source-chain outcome. Pending, + /// Payment received on its destination chain. Confirmed, + /// Source payment failed or was proven not to have executed. Failed, + /// Another operation consumed the payment nonce. Replaced, } #[derive(Clone, Debug, Serialize, Deserialize, uniffi::Record)] pub struct UsdtTransfer { pub id: String, - pub tx_hash: String, + /// Source transaction hash, absent until execution is observed. + pub tx_hash: Option, pub user_operation_hash: Option, pub recipient: String, pub amount: u64, @@ -42,5 +46,12 @@ pub struct UsdtTransfer { pub is_incoming: bool, pub status: UsdtTransferStatus, pub timestamp: u64, - pub explorer_url: String, +} + +impl UsdtTransfer { + pub(super) fn mark_unexecuted(&mut self, status: UsdtTransferStatus) { + self.status = status; + self.received_amount = 0; + self.fee = Some(0); + } } diff --git a/src/modules/usdt/wallet.rs b/src/modules/usdt/wallet.rs index 9970d5d..99a2863 100644 --- a/src/modules/usdt/wallet.rs +++ b/src/modules/usdt/wallet.rs @@ -1,12 +1,12 @@ use super::{ account::{validate_delegation, ENTRY_POINT}, amount::token_amount, - keys::{derive_key, parse_address}, + keys::{derive_owner_key, parse_address}, paymaster::{Pimlico, PAYMASTER}, rpc::Rpc, store::{QuoteData, Store}, - transaction::{event_data, EntryPoint, Erc20, Paymaster, Plan}, - types::{CHAIN_ID, EXPLORER, TOKEN}, + transaction::{entry_point_event, event_data, EntryPoint, Erc20, Paymaster, Plan}, + types::{CHAIN_ID, TOKEN}, user_operation::Authorization, UsdtError, UsdtQuote, UsdtTransfer, UsdtTransferStatus, }; @@ -16,6 +16,12 @@ use serde_json::{json, Value}; use std::sync::{atomic::AtomicU64, Arc}; use tokio::sync::Mutex; +const RECENT_EXECUTION_BLOCKS: u64 = 64; +const RECENT_EXECUTION_BUDGET: std::time::Duration = std::time::Duration::from_secs(5); +const NONCE_RECOVERY_BUDGET: std::time::Duration = std::time::Duration::from_secs(20); +const EXPIRY_SEARCH_BLOCKS: u64 = 4096; +const QUOTE_LIFETIME_SECONDS: u64 = 120; + #[derive(uniffi::Object)] pub struct UsdtWallet { pub(super) address: Address, @@ -28,6 +34,7 @@ pub struct UsdtWallet { #[uniffi::export(async_runtime = "tokio")] impl UsdtWallet { + /// Creates the sole owner of this wallet's database; reuse it for all calls until it is dropped. #[uniffi::constructor] pub fn new( address: String, @@ -111,8 +118,11 @@ impl UsdtWallet { .paymaster .prepare(self.address, nonce, authorization, &calls, timestamp) .await?; - let expires_at = - now().saturating_add(operation_expires_at.saturating_sub(timestamp).min(120)); + let expires_at = now().saturating_add( + operation_expires_at + .saturating_sub(timestamp) + .min(QUOTE_LIFETIME_SECONDS), + ); self.require_balance(amount, maximum_fee).await?; let quote = UsdtQuote { id: uuid::Uuid::new_v4().to_string(), @@ -132,6 +142,8 @@ impl UsdtWallet { Ok(quote) } + /// Repeating a quote ID returns its stored outcome, which may already be failed or replaced. + /// A pending outcome is durable and retryable; it does not imply bundler acceptance. pub async fn send( &self, quote_id: String, @@ -142,10 +154,7 @@ impl UsdtWallet { let passphrase = passphrase.map(zeroize::Zeroizing::new); let _guard = self.operation.lock().await; if let Some(existing) = self.store.transfer("e_id)? { - let key = derive_key(mnemonic, passphrase)?; - if super::keys::key_address(&key) != self.address { - return Err(UsdtError::InvalidCredentials); - } + derive_owner_key(mnemonic, passphrase, self.address)?; return Ok(existing); } self.store.require_no_pending()?; @@ -172,10 +181,7 @@ impl UsdtWallet { { return Err(UsdtError::QuoteExpired); } - let key = derive_key(mnemonic, passphrase)?; - if super::keys::key_address(&key) != self.address { - return Err(UsdtError::InvalidCredentials); - } + let key = derive_owner_key(mnemonic, passphrase, self.address)?; if data.quote.expires_at <= now() + 5 { return Err(UsdtError::QuoteExpired); } @@ -183,7 +189,7 @@ impl UsdtWallet { drop(key); let mut transfer = UsdtTransfer { id: quote_id, - tx_hash: String::new(), + tx_hash: None, user_operation_hash: Some(format!("{hash:#x}")), recipient: data.quote.recipient, amount: data.quote.amount, @@ -192,7 +198,6 @@ impl UsdtWallet { is_incoming: false, status: UsdtTransferStatus::Pending, timestamp: now(), - explorer_url: String::new(), }; self.store.record_signed(&transfer, &raw)?; // After persistence a lost response is indeterminate. Retry only the identical signed operation. @@ -202,9 +207,7 @@ impl UsdtWallet { error, UsdtError::QuoteExpired | UsdtError::UnsupportedDelegation ) { - transfer.status = UsdtTransferStatus::Failed; - transfer.received_amount = 0; - transfer.fee = Some(0); + transfer.mark_unexecuted(UsdtTransferStatus::Failed); self.store.update_transfer(&transfer)?; return Err(error); } @@ -212,9 +215,13 @@ impl UsdtWallet { Ok(transfer) } - /// Checks recent direct-payment execution at the current tip without scanning history or retrying submission. - /// Missing evidence leaves the signed payment pending; confirmation is L2 execution, not parent-chain finality. - pub async fn refresh_transfer(&self, id: String) -> Result, UsdtError> { + /// Checks recent direct-payment execution with a bounded request budget. + /// Requires the expected operation and transfer in a canonical receipt; current-tip execution is provisional. + /// Does not rebroadcast, expire payments or reconcile nonces. Missing evidence leaves the payment pending. + pub async fn check_recent_execution( + &self, + id: String, + ) -> Result, UsdtError> { let check = async { let _guard = self.operation.lock().await; let Some(mut transfer) = self.store.transfer(&id)? else { @@ -226,23 +233,20 @@ impl UsdtWallet { self.rpc.verify_chain().await?; let hash = plan.operation.hash(CHAIN_ID)?; let tip = self.block_number().await?; - let start = plan.created_block.max(tip.saturating_sub(63)); + let start = plan + .created_block + .max(tip.saturating_sub(RECENT_EXECUTION_BLOCKS - 1)); if start <= tip { - let logs: Vec = self.rpc.call("eth_getLogs", json!([{ - "address": ENTRY_POINT, "fromBlock": U256::from(start), "toBlock": U256::from(tip), - "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, hash, self.address.into_word()] - }])).await?; + let logs: Vec = self.operation_logs_in(start, tip, Some(hash)).await?; if let Some(log) = logs .iter() .find(|log| log["removed"].as_bool() != Some(true)) { - let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) - .map_err(|_| UsdtError::InvalidResponse)?; + let event = entry_point_event(log)?.ok_or(UsdtError::InvalidResponse)?; let number = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) .map_err(|_| UsdtError::InvalidResponse)?; - if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT - || event.userOpHash != hash + if event.userOpHash != hash || event.nonce != plan.operation.nonce || number < start || number > tip @@ -254,12 +258,14 @@ impl UsdtWallet { } Ok(Some(transfer)) }; - match tokio::time::timeout(std::time::Duration::from_secs(5), check).await { + match tokio::time::timeout(RECENT_EXECUTION_BUDGET, check).await { Ok(result) => result, Err(_) => Err(UsdtError::NetworkUnavailable), } } + /// Saves resumable history progress; returns true when caught up and false when more work remains. + /// Call between send flows. The soft budget permits an in-flight receipt to finish before yielding. pub async fn sync_history(&self) -> Result { let _guard = self.operation.lock().await; self.rpc.verify_chain().await?; @@ -270,117 +276,107 @@ impl UsdtWallet { self.store.transfers() } + /// Reconciles pending execution using chain proofs and may rebroadcast the identical signed operation. pub async fn refresh_transfers(&self) -> Result, UsdtError> { let _guard = self.operation.lock().await; - let transfers = self.store.unsettled()?; - if transfers.is_empty() { - return self.history(); + if let Some((mut transfer, plan)) = self.store.pending_operation()? { + self.recover_pending(&mut transfer, &plan).await?; } + self.history() + } +} + +impl UsdtWallet { + async fn recover_pending( + &self, + transfer: &mut UsdtTransfer, + plan: &Plan, + ) -> Result<(), UsdtError> { self.rpc.verify_chain().await?; - for mut transfer in transfers { - let Some(plan) = self.store.pending_plan(&transfer.id)? else { - continue; - }; - let hash = plan.operation.hash(CHAIN_ID)?; - let confirmed_tip = self.block_number().await?.saturating_sub(2); - if confirmed_tip < plan.created_block { - continue; + let hash = plan.operation.hash(CHAIN_ID)?; + let confirmed_tip = self.block_number().await?.saturating_sub(2); + if confirmed_tip < plan.created_block { + return Ok(()); + } + let end = self.pending_search_end(plan, confirmed_tip).await?; + let logs = match self + .operation_logs_in(plan.created_block, end, Some(hash)) + .await + { + Ok(logs) => logs, + // Discovery can be unavailable while independent nonce/receipt proofs still work. + Err(UsdtError::LogRangeTooLarge | UsdtError::NetworkUnavailable) => Vec::new(), + Err(error) => return Err(error), + }; + if let Some(log) = logs + .iter() + .find(|log| log["removed"].as_bool() != Some(true)) + { + let event = entry_point_event(log)?.ok_or(UsdtError::InvalidResponse)?; + if event.userOpHash != hash || event.nonce != plan.operation.nonce { + return Err(UsdtError::InvalidResponse); } - let end = self.pending_search_end(&plan, confirmed_tip).await?; - let logs: Vec = match self.rpc.call("eth_getLogs", json!([{ - "address": ENTRY_POINT, "fromBlock": U256::from(plan.created_block), "toBlock": U256::from(end), - "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, hash, self.address.into_word()] - }])).await { - Ok(logs) => logs, - // The nonce-based lookup below verifies the consuming event within a single block. - Err(UsdtError::LogRangeTooLarge) => Vec::new(), - Err(error) => return Err(error), - }; - if let Some(log) = logs - .iter() - .find(|log| log["removed"].as_bool() != Some(true)) - { - let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) - .map_err(|_| UsdtError::InvalidResponse)?; - if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT - || event.userOpHash != hash - || event.nonce != plan.operation.nonce - { - return Err(UsdtError::InvalidResponse); - } - self.settle_from_log(&mut transfer, log, event).await?; + return self.settle_from_log(transfer, log, event).await; + } + let nonce = self.nonce(&format!("0x{confirmed_tip:x}")).await?; + if nonce <= plan.operation.nonce { + if self.block_timestamp(confirmed_tip).await? > plan.expires_at { + transfer.mark_unexecuted(UsdtTransferStatus::Failed); + self.store.update_transfer(transfer)?; } else { - let nonce = self.nonce(&format!("0x{confirmed_tip:x}")).await?; - if nonce > plan.operation.nonce { - // A nonce advance alone cannot distinguish this payment from a replacement. - let block = self.nonce_consumed_block(&plan, confirmed_tip).await?; - let candidates: Vec = match self.rpc.call("eth_getLogs", json!([{ - "address": ENTRY_POINT, "fromBlock": U256::from(block), "toBlock": U256::from(block), - "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, null, self.address.into_word()] - }])).await { - Ok(logs) => logs, - Err(UsdtError::LogRangeTooLarge) => Vec::new(), - Err(error) => return Err(error), - }; - let mut matched = false; - for log in candidates - .iter() - .filter(|log| log["removed"].as_bool() != Some(true)) - { - let event = - EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) - .map_err(|_| UsdtError::InvalidResponse)?; - if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT - || u64::try_from(serde_json::from_value::( - log["blockNumber"].clone(), - )?) - .map_err(|_| UsdtError::InvalidResponse)? - != block - || event.sender != self.address - { - return Err(UsdtError::InvalidResponse); - } - if event.nonce != plan.operation.nonce { - continue; - } - if event.userOpHash == hash { - self.settle_from_log(&mut transfer, log, event).await?; - } else { - self.reconcile_consumed_nonce(&mut transfer, &plan, block) - .await?; - } - matched = true; - break; - } - if !matched { - self.reconcile_consumed_nonce(&mut transfer, &plan, block) - .await?; - } - } else { - let expired = self.block_timestamp(confirmed_tip).await? > plan.expires_at; - if expired { - transfer.status = UsdtTransferStatus::Failed; - transfer.received_amount = 0; - transfer.fee = Some(0); - self.store.update_transfer(&transfer)?; - } else { - let _ = self.broadcast(&plan, hash).await; - } - } + let _ = self.broadcast(plan, hash).await; } + return Ok(()); } - self.history() + // A nonce advance alone cannot distinguish this payment from a replacement. + let block = self.nonce_consumed_block(plan, confirmed_tip).await?; + let candidates = match self.operation_logs_in(block, block, None).await { + Ok(logs) => logs, + Err(UsdtError::LogRangeTooLarge | UsdtError::NetworkUnavailable) => Vec::new(), + Err(error) => return Err(error), + }; + for log in candidates + .iter() + .filter(|log| log["removed"].as_bool() != Some(true)) + { + let event = entry_point_event(log)?.ok_or(UsdtError::InvalidResponse)?; + if u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) + .map_err(|_| UsdtError::InvalidResponse)? + != block + || event.sender != self.address + { + return Err(UsdtError::InvalidResponse); + } + if event.nonce != plan.operation.nonce { + continue; + } + if event.userOpHash == hash { + return self.settle_from_log(transfer, log, event).await; + } + break; + } + self.reconcile_consumed_nonce(transfer, plan, block).await + } + + async fn operation_logs_in( + &self, + start: u64, + end: u64, + hash: Option, + ) -> Result, UsdtError> { + self.rpc.call("eth_getLogs", json!([{ + "address": ENTRY_POINT, "fromBlock": U256::from(start), "toBlock": U256::from(end), + "topics": [EntryPoint::UserOperationEvent::SIGNATURE_HASH, hash, self.address.into_word()] + }])).await } -} -impl UsdtWallet { async fn reconcile_consumed_nonce( &self, transfer: &mut UsdtTransfer, plan: &Plan, number: u64, ) -> Result<(), UsdtError> { - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(20); + let deadline = tokio::time::Instant::now() + NONCE_RECOVERY_BUDGET; let block = self.rpc.block(number).await?; let block_hash = format!("{:#x}", block.hash); let start = self.store.nonce_recovery(&transfer.id, &block_hash)?; @@ -396,11 +392,7 @@ impl UsdtWallet { .as_array() .ok_or(UsdtError::InvalidResponse)? { - if serde_json::from_value::
(log["address"].clone())? != ENTRY_POINT { - continue; - } - let Ok(event) = EntryPoint::UserOperationEvent::decode_log_data(&event_data(log)?) - else { + let Some(event) = entry_point_event(log)? else { continue; }; if event.sender != self.address || event.nonce != plan.operation.nonce { @@ -410,16 +402,12 @@ impl UsdtWallet { if event.paymaster != PAYMASTER { return Err(UsdtError::InvalidResponse); } - transfer.tx_hash = format!("{hash:#x}"); - transfer.explorer_url = format!("{EXPLORER}/tx/{hash:#x}"); + transfer.tx_hash = Some(format!("{hash:#x}")); self.settle(transfer, &receipt)?; } else { - transfer.status = UsdtTransferStatus::Replaced; - transfer.received_amount = 0; - transfer.fee = Some(0); + transfer.mark_unexecuted(UsdtTransferStatus::Replaced); } - transfer.timestamp = - u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; + transfer.timestamp = block.timestamp()?; return self.store.update_transfer(transfer); } self.store @@ -429,11 +417,8 @@ impl UsdtWallet { if self.rpc.block(number).await?.hash != block.hash { return Err(UsdtError::NetworkUnavailable); } - transfer.status = UsdtTransferStatus::Replaced; - transfer.received_amount = 0; - transfer.fee = Some(0); - transfer.timestamp = - u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; + transfer.mark_unexecuted(UsdtTransferStatus::Replaced); + transfer.timestamp = block.timestamp()?; self.store.update_transfer(transfer) } @@ -442,8 +427,7 @@ impl UsdtWallet { .map_err(|_| UsdtError::InvalidResponse) } pub(super) async fn block_timestamp(&self, number: u64) -> Result { - u64::try_from(self.rpc.block(number).await?.timestamp) - .map_err(|_| UsdtError::InvalidResponse) + self.rpc.block(number).await?.timestamp() } async fn token_balance(&self) -> Result { self.rpc @@ -463,8 +447,16 @@ impl UsdtWallet { Ok(()) } async fn nonce(&self, block: &str) -> Result { - let bytes: Bytes = self.rpc.call("eth_call", json!([{"to":ENTRY_POINT,"data":Bytes::from(EntryPoint::getNonceCall { sender:self.address, key:Default::default() }.abi_encode())}, block])).await?; - EntryPoint::getNonceCall::abi_decode_returns(&bytes).map_err(|_| UsdtError::InvalidResponse) + self.rpc + .contract_at( + ENTRY_POINT, + EntryPoint::getNonceCall { + sender: self.address, + key: Default::default(), + }, + block, + ) + .await } async fn authorization(&self) -> Result { let code: Bytes = self @@ -515,8 +507,7 @@ impl UsdtWallet { return Err(UsdtError::InvalidResponse); } let hash: B256 = serde_json::from_value(log["transactionHash"].clone())?; - transfer.tx_hash = format!("{hash:#x}"); - transfer.explorer_url = format!("{EXPLORER}/tx/{}", transfer.tx_hash); + transfer.tx_hash = Some(format!("{hash:#x}")); let number = u64::try_from(serde_json::from_value::(log["blockNumber"].clone())?) .map_err(|_| UsdtError::InvalidResponse)?; let block = self.rpc.block(number).await?; @@ -525,8 +516,7 @@ impl UsdtWallet { } let receipt = self.rpc.block_receipt(hash, block.hash, number).await?; self.settle(transfer, &receipt)?; - transfer.timestamp = - u64::try_from(block.timestamp).map_err(|_| UsdtError::InvalidResponse)?; + transfer.timestamp = block.timestamp()?; self.store.update_transfer(transfer) } @@ -547,7 +537,7 @@ impl UsdtWallet { async fn pending_search_end(&self, plan: &Plan, tip: u64) -> Result { // The paymaster validity window bounds recovery even after a long absence. - if tip <= plan.created_block + 4096 { + if tip <= plan.created_block + EXPIRY_SEARCH_BLOCKS { return Ok(tip); } let mut low = plan.created_block; @@ -574,14 +564,9 @@ impl UsdtWallet { .parse() .map_err(|_| UsdtError::InvalidResponse)?; let logs = super::transaction::operation_logs(receipt, operation_hash)?; - let event = EntryPoint::UserOperationEvent::decode_log_data(&event_data( - logs.last().ok_or(UsdtError::InvalidResponse)?, - )?) - .map_err(|_| UsdtError::InvalidResponse)?; - if event.userOpHash != operation_hash - || event.sender != self.address - || event.paymaster != PAYMASTER - { + let event = entry_point_event(logs.last().ok_or(UsdtError::InvalidResponse)?)? + .ok_or(UsdtError::InvalidResponse)?; + if event.sender != self.address || event.paymaster != PAYMASTER { return Err(UsdtError::InvalidResponse); } let mut transfer_proven = false; From ccdd478372560717263ef60ae3eb7139261ea575 Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 25 Sep 2026 16:35:01 +0300 Subject: [PATCH 9/9] fix: exclude zero and self transfers from restored payments --- Package.swift | 2 +- src/modules/usdt/history.rs | 6 +++--- src/modules/usdt/tests.rs | 34 ++++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Package.swift b/Package.swift index 8417027..794f6ec 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription import Foundation let tag = "v0.6.0" -let checksum = "0468d0d735745d36079ea0e7f03cb73b3c284376a296cdb2c7836e511a2a284b" +let checksum = "ac07f72265cde752e9f7951383e8d1a01f3568676d156ee592a715bc5a38d32a" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let localBinary = ProcessInfo.processInfo.environment["BITKIT_CORE_LOCAL"] == "1" diff --git a/src/modules/usdt/history.rs b/src/modules/usdt/history.rs index e198538..8899563 100644 --- a/src/modules/usdt/history.rs +++ b/src/modules/usdt/history.rs @@ -336,7 +336,7 @@ impl UsdtWallet { if !super::paymaster::supported_payment(&op.paymasterAndData) { continue; } - let Some((recipient, amount)) = decode_payment(&op.callData) else { + let Some((recipient, amount)) = decode_payment(&op.callData, self.address) else { continue; }; (recipient.to_checksum(None), amount) @@ -369,7 +369,7 @@ impl UsdtWallet { } } -fn decode_payment(data: &[u8]) -> Option<(Address, u64)> { +fn decode_payment(data: &[u8], sender: Address) -> Option<(Address, u64)> { let calls = decode_calls(data).ok()?; let mut payment = None; for (target, data) in calls { @@ -377,7 +377,7 @@ fn decode_payment(data: &[u8]) -> Option<(Address, u64)> { return None; } if let Ok(call) = Erc20::transferCall::abi_decode(&data) { - if payment.is_some() { + if payment.is_some() || call.amount.is_zero() || call.recipient == sender { return None; } payment = Some((call.recipient, token_amount(call.amount).ok()?)); diff --git a/src/modules/usdt/tests.rs b/src/modules/usdt/tests.rs index d016869..4285379 100644 --- a/src/modules/usdt/tests.rs +++ b/src/modules/usdt/tests.rs @@ -2362,9 +2362,9 @@ async fn settlement_uses_the_canonical_operation_outcome() { } #[tokio::test] -async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { +async fn unrecognized_operations_preserve_raw_debits_and_refunds() { use alloy_primitives::{Address, U256}; - use alloy_sol_types::SolEvent; + use alloy_sol_types::{SolCall, SolEvent}; use serde_json::json; let chain = MockChain::start().await; let dir = tempfile::tempdir().unwrap(); @@ -2386,7 +2386,13 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { .encode_log_data(); json!({"address":types::TOKEN,"topics":event.topics(),"data":event.data,"logIndex":U256::from(index)}) }; - for flags in [2u8, 4u8] { + let recipient = RECIPIENT.parse::
().unwrap(); + for (flags, recipient, amount, expected_outgoing) in [ + (2u8, recipient, 1_000_000u64, 1_000_200), + (4, recipient, 1_000_000, 1_000_200), + (0, recipient, 0, 200), + (0, wallet.address, 1_000_000, 200), + ] { { let mut state = chain.state.lock().unwrap(); state.mined = true; @@ -2394,21 +2400,22 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { let mut data = state.operations[0].paymaster_data.to_vec(); data[1] = flags; state.operations[0].paymaster_data = data.into(); + state.operations[0].call_data = account::batch(&[( + types::TOKEN, + transaction::Erc20::transferCall { + recipient, + amount: U256::from(amount), + } + .abi_encode() + .into(), + )]); let mut logs = state.event_logs(); logs.retain(|log| log["address"] != json!(types::TOKEN)); logs.insert( 0, movement(wallet.address, paymaster::PAYMASTER, 200u64, 2u64), ); - logs.insert( - 1, - movement( - wallet.address, - RECIPIENT.parse::
().unwrap(), - 1_000_000u64, - 3u64, - ), - ); + logs.insert(1, movement(wallet.address, recipient, amount, 3u64)); logs.insert( 2, movement(paymaster::PAYMASTER, wallet.address, 50u64, 4u64), @@ -2419,7 +2426,6 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { let restored = chain.wallet(&restored_dir); assert!(restored.sync_history().await.unwrap()); let history = restored.history().unwrap(); - assert_eq!(history.len(), 3); assert!(history.iter().all(|row| row.user_operation_hash.is_none())); assert_eq!( history @@ -2435,7 +2441,7 @@ async fn external_paymaster_modes_preserve_raw_debits_and_refunds() { .filter(|row| !row.is_incoming) .map(|row| row.amount) .sum::(), - 1_000_200 + expected_outgoing ); } }