diff --git a/lib/Cargo.lock b/lib/Cargo.lock index bb0eccb43..5b05baddf 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -188,38 +188,16 @@ dependencies = [ "backtrace", ] -[[package]] -name = "askama" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139" -dependencies = [ - "askama_derive 0.11.2", - "askama_escape", - "askama_shared", -] - [[package]] name = "askama" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" dependencies = [ - "askama_derive 0.12.5", + "askama_derive", "askama_escape", ] -[[package]] -name = "askama_derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71" -dependencies = [ - "askama_shared", - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "askama_derive" version = "0.12.5" @@ -251,23 +229,6 @@ dependencies = [ "nom", ] -[[package]] -name = "askama_shared" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0" -dependencies = [ - "askama_escape", - "mime", - "mime_guess", - "nom", - "proc-macro2", - "quote", - "serde", - "syn 1.0.109", - "toml", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -443,21 +404,23 @@ name = "bindings-react-native" version = "0.3.4" dependencies = [ "anyhow", - "askama 0.11.1", + "askama", "camino", "clap 3.2.25", "heck 0.4.1", "log", "once_cell", "paste", + "regex", "serde", + "textwrap", "thiserror", "tokio", "toml", - "uniffi 0.23.0", - "uniffi_bindgen 0.23.0", - "uniffi_build 0.23.0", - "uniffi_macros 0.23.0", + "uniffi", + "uniffi_bindgen 0.3.1+v0.25.0", + "uniffi_build", + "uniffi_macros", ] [[package]] @@ -686,8 +649,9 @@ dependencies = [ "once_cell", "thiserror", "tokio", - "uniffi 0.25.3", + "uniffi", "uniffi_bindgen 0.25.3", + "uniffi_bindgen 0.3.1+v0.25.0", "uniffi_bindgen_kotlin_multiplatform", ] @@ -2211,10 +2175,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "oneshot-uniffi" -version = "0.1.6" +name = "oneshot" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c" +checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" [[package]] name = "opaque-debug" @@ -2696,9 +2660,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -2708,9 +2672,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -2725,9 +2689,9 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "remove_dir_all" @@ -3339,6 +3303,12 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.7" @@ -3516,6 +3486,11 @@ name = "textwrap" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] [[package]] name = "thiserror" @@ -3855,6 +3830,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -3865,44 +3846,35 @@ dependencies = [ ] [[package]] -name = "uniffi" -version = "0.23.0" +name = "unicode-width" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71cc01459bc34cfe43fabf32b39f1228709bc6db1b3a664a92940af3d062376" -dependencies = [ - "anyhow", - "camino", - "clap 3.2.25", - "uniffi_bindgen 0.23.0", - "uniffi_core 0.23.0", - "uniffi_macros 0.23.0", -] +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "uniffi" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21345172d31092fd48c47fd56c53d4ae9e41c4b1f559fb8c38c1ab1685fd919f" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "anyhow", "camino", "clap 4.5.17", - "uniffi_bindgen 0.25.3", - "uniffi_build 0.25.3", - "uniffi_core 0.25.3", - "uniffi_macros 0.25.3", + "uniffi_bindgen 0.3.1+v0.25.0", + "uniffi_build", + "uniffi_core", + "uniffi_macros", ] [[package]] name = "uniffi_bindgen" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbba5103051c18f10b22f80a74439ddf7100273f217a547005d2735b2498994" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "anyhow", - "askama 0.11.1", - "bincode", + "askama", "camino", + "cargo_metadata", + "clap 4.5.17", "fs-err", "glob", "goblin", @@ -3910,11 +3882,11 @@ dependencies = [ "once_cell", "paste", "serde", - "serde_json", + "textwrap", "toml", - "uniffi_meta 0.23.0", - "uniffi_testing 0.23.0", - "weedle2", + "uniffi_meta 0.3.1+v0.25.0", + "uniffi_testing 0.3.1+v0.25.0", + "uniffi_udl 0.3.1+v0.25.0", ] [[package]] @@ -3924,10 +3896,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd992f2929a053829d5875af1eff2ee3d7a7001cb3b9a46cc7895f2caede6940" dependencies = [ "anyhow", - "askama 0.12.1", + "askama", "camino", "cargo_metadata", - "clap 4.5.17", "fs-err", "glob", "goblin", @@ -3938,7 +3909,7 @@ dependencies = [ "toml", "uniffi_meta 0.25.3", "uniffi_testing 0.25.3", - "uniffi_udl", + "uniffi_udl 0.25.3", ] [[package]] @@ -3947,7 +3918,7 @@ version = "0.1.0" source = "git+https://gitlab.com/trixnity/uniffi-kotlin-multiplatform-bindings?rev=e8e3a88df5b657787c1198425c16008232b26548#e8e3a88df5b657787c1198425c16008232b26548" dependencies = [ "anyhow", - "askama 0.12.1", + "askama", "camino", "clap 4.5.17", "heck 0.4.1", @@ -3960,34 +3931,21 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1a28368ff3d83717e3d3e2e15a66269c43488c3f036914131bb68892f29fb" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "anyhow", "camino", - "uniffi_bindgen 0.23.0", -] - -[[package]] -name = "uniffi_build" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001964dd3682d600084b3aaf75acf9c3426699bc27b65e96bb32d175a31c74e9" -dependencies = [ - "anyhow", - "camino", - "uniffi_bindgen 0.25.3", + "uniffi_bindgen 0.3.1+v0.25.0", ] [[package]] name = "uniffi_checksum_derive" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03de61393a42b4ad4984a3763c0600594ac3e57e5aaa1d05cede933958987c03" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -4002,60 +3960,23 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2b4852d638d74ca2d70e450475efb6d91fe6d54a7cd8d6bd80ad2ee6cd7daa" -dependencies = [ - "anyhow", - "bytes", - "camino", - "cargo_metadata", - "log", - "once_cell", - "paste", - "static_assertions", -] - -[[package]] -name = "uniffi_core" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6121a127a3af1665cd90d12dd2b3683c2643c5103281d0fed5838324ca1fad5b" +version = "0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "anyhow", "bytes", "camino", "log", "once_cell", - "oneshot-uniffi", + "oneshot", "paste", "static_assertions", ] [[package]] name = "uniffi_macros" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa03394de21e759e0022f1ea8d992d2e39290d735b9ed52b1f74b20a684f794e" -dependencies = [ - "bincode", - "camino", - "fs-err", - "once_cell", - "proc-macro2", - "quote", - "serde", - "syn 1.0.109", - "toml", - "uniffi_build 0.23.0", - "uniffi_meta 0.23.0", -] - -[[package]] -name = "uniffi_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cf7a58f101fcedafa5b77ea037999b88748607f0ef3a33eaa0efc5392e92e4" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "bincode", "camino", @@ -4066,19 +3987,19 @@ dependencies = [ "serde", "syn 2.0.77", "toml", - "uniffi_build 0.25.3", - "uniffi_meta 0.25.3", + "uniffi_build", + "uniffi_meta 0.3.1+v0.25.0", ] [[package]] name = "uniffi_meta" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fdab2c436aed7a6391bec64204ec33948bfed9b11b303235740771f85c4ea6" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ - "serde", + "anyhow", + "bytes", "siphasher", - "uniffi_checksum_derive 0.23.0", + "uniffi_checksum_derive 0.3.1+v0.25.0", ] [[package]] @@ -4095,17 +4016,14 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b0570953ec41d97ce23e3b92161ac18231670a1f97523258a6d2ab76d7f76c" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" dependencies = [ "anyhow", "camino", "cargo_metadata", "fs-err", "once_cell", - "serde", - "serde_json", ] [[package]] @@ -4121,6 +4039,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "uniffi_udl" +version = "0.3.1+v0.25.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" +dependencies = [ + "anyhow", + "uniffi_meta 0.3.1+v0.25.0", + "uniffi_testing 0.3.1+v0.25.0", + "weedle2 4.0.0 (git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0)", +] + [[package]] name = "uniffi_udl" version = "0.25.3" @@ -4130,7 +4059,7 @@ dependencies = [ "anyhow", "uniffi_meta 0.25.3", "uniffi_testing 0.25.3", - "weedle2", + "weedle2 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4374,6 +4303,14 @@ dependencies = [ "nom", ] +[[package]] +name = "weedle2" +version = "4.0.0" +source = "git+https://github.com/NordSecurity/uniffi-rs.git?tag=v0.3.1+v0.25.0#1a5dc527165589456c3d7233107917e065192b03" +dependencies = [ + "nom", +] + [[package]] name = "which" version = "4.4.2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 9ef470383..c5c53cc80 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -36,8 +36,10 @@ log = "0.4.20" once_cell = "1.19" thiserror = "1.0" # Version must match that used by uniffi-bindgen-go -uniffi = "0.25.0" -uniffi_macros = "0.25.0" +uniffi = { git = "https://github.com/NordSecurity/uniffi-rs.git", tag = "v0.3.1+v0.25.0" } +uniffi_bindgen = { git = "https://github.com/NordSecurity/uniffi-rs.git", tag = "v0.3.1+v0.25.0" } +uniffi_build = { git = "https://github.com/NordSecurity/uniffi-rs.git", tag = "v0.3.1+v0.25.0" } +uniffi_macros = { git = "https://github.com/NordSecurity/uniffi-rs.git", tag = "v0.3.1+v0.25.0" } [patch.crates-io] # https://github.com/BlockstreamResearch/rust-secp256k1-zkp/pull/48/commits diff --git a/lib/bindings/Cargo.toml b/lib/bindings/Cargo.toml index 0f713421d..ad297a5d1 100644 --- a/lib/bindings/Cargo.toml +++ b/lib/bindings/Cargo.toml @@ -17,7 +17,8 @@ breez-sdk-liquid = { path = "../core" } log = { workspace = true } uniffi = { workspace = true, features = [ "bindgen-tests", "cli" ] } # Bindgen used by KMP, version has to match the one supported by KMP -uniffi_bindgen = "0.25.2" +uniffi_bindgen = { workspace = true } +uniffi_bindgen_kmp = { version = "0.25.2", package = "uniffi_bindgen" } uniffi_bindgen_kotlin_multiplatform = { git = "https://gitlab.com/trixnity/uniffi-kotlin-multiplatform-bindings", rev = "e8e3a88df5b657787c1198425c16008232b26548" } camino = "1.1.1" thiserror = { workspace = true } diff --git a/lib/bindings/langs/flutter/scripts/pubspec.lock b/lib/bindings/langs/flutter/scripts/pubspec.lock new file mode 100644 index 000000000..1c109f024 --- /dev/null +++ b/lib/bindings/langs/flutter/scripts/pubspec.lock @@ -0,0 +1,117 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: "direct main" + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" + source: hosted + version: "1.3.1" + cli_script: + dependency: "direct main" + description: + name: cli_script + sha256: "3463c6e8e57271faaf557eee56cb455522f1ab1ebe618bbfb7454f74fc793967" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + glob: + dependency: "direct main" + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" +sdks: + dart: ">=3.4.0 <4.0.0" diff --git a/lib/bindings/langs/react-native/Cargo.toml b/lib/bindings/langs/react-native/Cargo.toml index e5caa534a..941b1caf3 100644 --- a/lib/bindings/langs/react-native/Cargo.toml +++ b/lib/bindings/langs/react-native/Cargo.toml @@ -9,20 +9,22 @@ edition = "2021" anyhow = { version = "1.0.57", features = ["backtrace"] } thiserror = "1.0" tokio = { version = "1", features = ["full"] } -uniffi = { version = "0.23.0", features = ["bindgen-tests", "cli"] } -uniffi_bindgen = "0.23.0" -uniffi_macros = "0.23.0" +uniffi = { workspace = true, features = ["bindgen-tests", "cli"] } +uniffi_bindgen = { workspace = true } +uniffi_macros = { workspace = true } camino = "1.1.1" log = { workspace = true } serde = "*" -askama = { version = "0.11.1", default-features = false, features = ["config"] } +askama = { version = "0.12", default-features = false, features = ["config"] } toml = "0.5" clap = { version = "3.2.22", features = ["derive"] } heck = "0.4" paste = "1.0" once_cell = { workspace = true } +textwrap = "0.16" +regex = "1.11.0" [build-dependencies] -uniffi_build = { version = "0.23.0" } -uniffi_bindgen = "0.23.0" +uniffi_build = { workspace = true } +uniffi_bindgen = { workspace = true } anyhow = { version = "1.0.57", features = ["backtrace"] } diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/callback_interface.rs b/lib/bindings/langs/react-native/src/gen_kotlin/callback_interface.rs new file mode 100644 index 000000000..1bffcf46e --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/callback_interface.rs @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::ComponentInterface; + +use super::CodeType; + +#[derive(Debug)] +pub struct CallbackInterfaceCodeType { + id: String, +} + +impl CallbackInterfaceCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for CallbackInterfaceCodeType { + fn type_label(&self, ci: &ComponentInterface) -> String { + super::KotlinCodeOracle.class_name(ci, &self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } + + fn initialization_fn(&self) -> Option { + Some(format!("{}.register", self.ffi_converter_name())) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/compounds.rs b/lib/bindings/langs/react-native/src/gen_kotlin/compounds.rs new file mode 100644 index 000000000..5208590e2 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/compounds.rs @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use paste::paste; +use uniffi_bindgen::{ + backend::{Literal, Type}, + ComponentInterface, +}; + +#[allow(dead_code)] +fn render_literal(literal: &Literal, inner: &Type, ci: &ComponentInterface) -> String { + match literal { + Literal::Null => "null".into(), + Literal::EmptySequence => "listOf()".into(), + Literal::EmptyMap => "mapOf()".into(), + + // For optionals + _ => super::KotlinCodeOracle.find(inner).literal(literal, ci), + } +} + +macro_rules! impl_code_type_for_compound { + ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => { + paste! { + #[derive(Debug)] + pub struct $T { + inner: Type, + } + + impl $T { + pub fn new(inner: Type) -> Self { + Self { inner } + } + fn inner(&self) -> &Type { + &self.inner + } + } + + impl CodeType for $T { + fn type_label(&self, ci: &ComponentInterface) -> String { + format!($type_label_pattern, super::KotlinCodeOracle.find(self.inner()).type_label(ci)) + } + + fn canonical_name(&self) -> String { + format!($canonical_name_pattern, super::KotlinCodeOracle.find(self.inner()).canonical_name()) + } + + fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { + render_literal(literal, self.inner(), ci) + } + } + } + } + } + +impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}"); +impl_code_type_for_compound!(SequenceCodeType, "List<{}>", "Sequence{}"); + +#[derive(Debug)] +pub struct MapCodeType { + key: Type, + value: Type, +} + +impl MapCodeType { + pub fn new(key: Type, value: Type) -> Self { + Self { key, value } + } + + fn key(&self) -> &Type { + &self.key + } + + fn value(&self) -> &Type { + &self.value + } +} + +impl CodeType for MapCodeType { + fn type_label(&self, ci: &ComponentInterface) -> String { + format!( + "Map<{}, {}>", + super::KotlinCodeOracle.find(self.key()).type_label(ci), + super::KotlinCodeOracle.find(self.value()).type_label(ci), + ) + } + + fn canonical_name(&self) -> String { + format!( + "Map{}{}", + super::KotlinCodeOracle.find(self.key()).canonical_name(), + super::KotlinCodeOracle.find(self.value()).canonical_name(), + ) + } + + fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { + render_literal(literal, &self.value, ci) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/custom.rs b/lib/bindings/langs/react-native/src/gen_kotlin/custom.rs new file mode 100644 index 000000000..69ac74ec0 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/custom.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::ComponentInterface; + +use super::CodeType; + +#[derive(Debug)] +pub struct CustomCodeType { + name: String, +} + +impl CustomCodeType { + pub fn new(name: String) -> Self { + CustomCodeType { name } + } +} + +impl CodeType for CustomCodeType { + fn type_label(&self, _ci: &ComponentInterface) -> String { + self.name.clone() + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.name) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/enum_.rs b/lib/bindings/langs/react-native/src/gen_kotlin/enum_.rs new file mode 100644 index 000000000..24db8445b --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/enum_.rs @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::{backend::Literal, ComponentInterface}; + +use super::CodeType; + +#[derive(Debug)] +pub struct EnumCodeType { + id: String, +} + +impl EnumCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for EnumCodeType { + fn type_label(&self, ci: &ComponentInterface) -> String { + super::KotlinCodeOracle.class_name(ci, &self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } + + fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { + if let Literal::Enum(v, _) = literal { + format!( + "{}.{}", + self.type_label(ci), + super::KotlinCodeOracle.enum_variant_name(v) + ) + } else { + unreachable!(); + } + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/executor.rs b/lib/bindings/langs/react-native/src/gen_kotlin/executor.rs new file mode 100644 index 000000000..b79773688 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/executor.rs @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::ComponentInterface; + +use super::CodeType; + +#[derive(Debug)] +pub struct ForeignExecutorCodeType; + +impl CodeType for ForeignExecutorCodeType { + fn type_label(&self, _ci: &ComponentInterface) -> String { + // Kotlin uses a CoroutineScope for ForeignExecutor + "CoroutineScope".into() + } + + fn canonical_name(&self) -> String { + "ForeignExecutor".into() + } + + fn initialization_fn(&self) -> Option { + Some("FfiConverterForeignExecutor.register".into()) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/external.rs b/lib/bindings/langs/react-native/src/gen_kotlin/external.rs new file mode 100644 index 000000000..046f21837 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/external.rs @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::ComponentInterface; + +use super::CodeType; + +#[derive(Debug)] +pub struct ExternalCodeType { + name: String, +} + +impl ExternalCodeType { + pub fn new(name: String) -> Self { + Self { name } + } +} + +impl CodeType for ExternalCodeType { + fn type_label(&self, _ci: &ComponentInterface) -> String { + self.name.clone() + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.name) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/miscellany.rs b/lib/bindings/langs/react-native/src/gen_kotlin/miscellany.rs new file mode 100644 index 000000000..30cd17a8d --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/miscellany.rs @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use paste::paste; +use uniffi_bindgen::ComponentInterface; + +macro_rules! impl_code_type_for_miscellany { + ($T:ty, $class_name:literal, $canonical_name:literal) => { + paste! { + #[derive(Debug)] + pub struct $T; + + impl CodeType for $T { + fn type_label(&self, _ci: &ComponentInterface) -> String { + $class_name.into() + } + + fn canonical_name(&self) -> String { + $canonical_name.into() + } + } + } + }; +} + +impl_code_type_for_miscellany!(TimestampCodeType, "java.time.Instant", "Timestamp"); + +impl_code_type_for_miscellany!(DurationCodeType, "java.time.Duration", "Duration"); diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs index 300044238..e2d2a3518 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs @@ -1,13 +1,65 @@ use std::cell::RefCell; use std::collections::{BTreeSet, HashSet}; +use std::fmt::Debug; use askama::Template; +use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use once_cell::sync::Lazy; use uniffi_bindgen::interface::*; -pub use uniffi_bindgen::bindings::kotlin::gen_kotlin::*; +use crate::generator::Config; -use crate::generator::RNConfig; +mod callback_interface; +mod compounds; +mod custom; +mod enum_; +mod executor; +mod external; +mod miscellany; +mod object; +mod primitives; +mod record; + +#[allow(dead_code)] +trait CodeType: Debug { + /// The language specific label used to reference this type. This will be used in + /// method signatures and property declarations. + fn type_label(&self, ci: &ComponentInterface) -> String; + + /// A representation of this type label that can be used as part of another + /// identifier. e.g. `read_foo()`, or `FooInternals`. + /// + /// This is especially useful when creating specialized objects or methods to deal + /// with this type only. + fn canonical_name(&self) -> String; + + fn literal(&self, _literal: &Literal, ci: &ComponentInterface) -> String { + unimplemented!("Unimplemented for {}", self.type_label(ci)) + } + + /// Name of the FfiConverter + /// + /// This is the object that contains the lower, write, lift, and read methods for this type. + /// Depending on the binding this will either be a singleton or a class with static methods. + /// + /// This is the newer way of handling these methods and replaces the lower, write, lift, and + /// read CodeType methods. Currently only used by Kotlin, but the plan is to move other + /// backends to using this. + fn ffi_converter_name(&self) -> String { + format!("FfiConverter{}", self.canonical_name()) + } + + /// A list of imports that are needed if this type is in use. + /// Classes are imported exactly once. + fn imports(&self) -> Option> { + None + } + + /// Function to run at startup + fn initialization_fn(&self) -> Option { + None + } +} static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { let list: Vec<&str> = vec!["connect", "add_event_listener", "set_logger"]; @@ -18,14 +70,14 @@ static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { #[template(syntax = "rn", escape = "none", path = "mapper.kt")] #[allow(dead_code)] pub struct MapperGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, // Track types used in sequences with the `add_sequence_type()` macro sequence_types: RefCell>, } impl<'a> MapperGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci, @@ -57,29 +109,117 @@ impl<'a> MapperGenerator<'a> { #[template(syntax = "rn", escape = "none", path = "module.kt")] #[allow(dead_code)] pub struct ModuleGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, } impl<'a> ModuleGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci } } } -pub mod filters { - use heck::*; - use uniffi_bindgen::backend::CodeOracle; - use uniffi_bindgen::backend::{CodeType, TypeIdentifier}; +#[derive(Clone)] +pub struct KotlinCodeOracle; + +#[allow(dead_code)] +impl KotlinCodeOracle { + // Map `Type` instances to a `Box` for that type. + // + // There is a companion match in `templates/Types.kt` which performs a similar function for the + // template code. + // + // - When adding additional types here, make sure to also add a match arm to the `Types.kt` template. + // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches + fn create_code_type(&self, type_: Type) -> Box { + match type_ { + Type::UInt8 => Box::new(primitives::UInt8CodeType), + Type::Int8 => Box::new(primitives::Int8CodeType), + Type::UInt16 => Box::new(primitives::UInt16CodeType), + Type::Int16 => Box::new(primitives::Int16CodeType), + Type::UInt32 => Box::new(primitives::UInt32CodeType), + Type::Int32 => Box::new(primitives::Int32CodeType), + Type::UInt64 => Box::new(primitives::UInt64CodeType), + Type::Int64 => Box::new(primitives::Int64CodeType), + Type::Float32 => Box::new(primitives::Float32CodeType), + Type::Float64 => Box::new(primitives::Float64CodeType), + Type::Boolean => Box::new(primitives::BooleanCodeType), + Type::String => Box::new(primitives::StringCodeType), + Type::Bytes => Box::new(primitives::BytesCodeType), + + Type::Timestamp => Box::new(miscellany::TimestampCodeType), + Type::Duration => Box::new(miscellany::DurationCodeType), + + Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)), + Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)), + Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)), + Type::CallbackInterface { name, .. } => { + Box::new(callback_interface::CallbackInterfaceCodeType::new(name)) + } + Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType), + Type::Optional { inner_type } => { + Box::new(compounds::OptionalCodeType::new(*inner_type)) + } + Type::Sequence { inner_type } => { + Box::new(compounds::SequenceCodeType::new(*inner_type)) + } + Type::Map { + key_type, + value_type, + } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)), + Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)), + Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)), + } + } + + fn find(&self, type_: &impl AsType) -> Box { + self.create_code_type(type_.as_type()) + } + + /// Get the idiomatic Kotlin rendering of a class name (for enums, records, errors, etc). + fn class_name(&self, ci: &ComponentInterface, nm: &str) -> String { + let name = nm.to_string().to_upper_camel_case(); + // fixup errors. + ci.is_name_used_as_error(nm) + .then(|| self.convert_error_suffix(&name)) + .unwrap_or(name) + } + + fn convert_error_suffix(&self, nm: &str) -> String { + match nm.strip_suffix("Error") { + None => nm.to_string(), + Some(stripped) => format!("{stripped}Exception"), + } + } + + /// Get the idiomatic Kotlin rendering of a function name. + fn fn_name(&self, nm: &str) -> String { + format!("`{}`", nm.to_string().to_lower_camel_case()) + } + /// Get the idiomatic Kotlin rendering of a variable name. + fn var_name(&self, nm: &str) -> String { + format!("`{}`", nm.to_string().to_lower_camel_case()) + } + + /// Get the idiomatic Kotlin rendering of an individual enum variant. + fn enum_variant_name(&self, nm: &str) -> String { + nm.to_string().to_shouty_snake_case() + } +} + +pub mod filters { use super::*; fn oracle() -> &'static KotlinCodeOracle { &KotlinCodeOracle } - pub fn type_name(codetype: &impl CodeType) -> Result { - Ok(codetype.type_label(oracle())) + pub fn type_name( + type_: &impl AsType, + ci: &ComponentInterface, + ) -> Result { + Ok(oracle().find(type_).type_label(ci)) } pub fn fn_name(nm: &str) -> Result { @@ -103,8 +243,8 @@ pub mod filters { "ULong" => Ok("array.pushDouble(value.toDouble())".to_string()), _ => match ci.get_type(type_name) { Some(t) => match t { - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(&inner).unwrap(); + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { true => Ok("array.pushString(value.name.lowercase())".to_string()), false => Ok("array.pushMap(readableMapOf(value))".to_string()), @@ -119,13 +259,13 @@ pub mod filters { } pub fn render_to_map( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, obj_name: &str, field_name: &str, optional: bool, ) -> Result { - let res: Result = match t { + let res: Result = match type_.as_type() { Type::UInt8 => Ok(format!("{obj_name}.{field_name}")), Type::Int8 => Ok(format!("{obj_name}.{field_name}")), Type::UInt16 => Ok(format!("{obj_name}.{field_name}")), @@ -138,17 +278,18 @@ pub mod filters { Type::Float64 => Ok(format!("{obj_name}.{field_name}")), Type::Boolean => Ok(format!("{obj_name}.{field_name}")), Type::String => Ok(format!("{obj_name}.{field_name}")), + Type::Bytes => Ok(format!("{obj_name}.{field_name}")), Type::Timestamp => unimplemented!("render_to_map: Timestamp is not implemented"), Type::Duration => unimplemented!("render_to_map: Duration is not implemented"), - Type::Object(_) => unimplemented!("render_to_map: Object is not implemented"), - Type::Record(_) => match optional { + Type::Object { .. } => unimplemented!("render_to_map: Object is not implemented"), + Type::Record { .. } => match optional { true => Ok(format!( "{obj_name}.{field_name}?.let {{ readableMapOf(it) }}" )), false => Ok(format!("readableMapOf({obj_name}.{field_name})")), }, - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { true => match optional { true => Ok(format!( @@ -164,36 +305,35 @@ pub mod filters { }, } } - Type::Error(_) => unimplemented!("render_to_map: Error is not implemented"), - Type::CallbackInterface(_) => { + Type::CallbackInterface { .. } => { unimplemented!("render_to_map: CallbackInterface is not implemented") } - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::ForeignExecutor { .. } => { + unimplemented!("render_to_map: ForeignExecutor is not implemented") + } + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); render_to_map(unboxed, ci, obj_name, field_name, true) } - Type::Sequence(_) => match optional { + Type::Sequence { .. } => match optional { true => Ok(format!( "{obj_name}.{field_name}?.let {{ readableArrayOf(it) }}" )), false => Ok(format!("readableArrayOf({obj_name}.{field_name})")), }, - Type::Map(_, _) => unimplemented!("render_to_map: Map is not implemented"), + Type::Map { .. } => unimplemented!("render_to_map: Map is not implemented"), Type::External { .. } => { unimplemented!("render_to_map: External is not implemented") } Type::Custom { .. } => { unimplemented!("render_to_map: Custom is not implemented") } - Type::Unresolved { .. } => { - unimplemented!("render_to_map: Unresolved is not implemented") - } }; res } pub fn render_from_map( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, name: &str, field_name: &str, @@ -203,7 +343,7 @@ pub mod filters { if !optional { mandatory_suffix = "!!" } - let res: String = match t { + let res: String = match type_.as_type() { Type::UInt8 => format!("{name}.getInt(\"{field_name}\").toUByte()"), Type::Int8 => format!("{name}.getInt(\"{field_name}\").toByte()"), Type::UInt16 => format!("{name}.getInt(\"{field_name}\").toUShort()"), @@ -216,49 +356,51 @@ pub mod filters { Type::Float64 => format!("{name}.getDouble(\"{field_name}\")"), Type::Boolean => format!("{name}.getBoolean(\"{field_name}\")"), Type::String => format!("{name}.getString(\"{field_name}\"){mandatory_suffix}"), + Type::Bytes => format!("{name}.getString(\"{field_name}\").toByteArray()"), Type::Timestamp => "".into(), Type::Duration => "".into(), - Type::Object(_) => "".into(), - Type::Record(_) => { - let record_type_name = type_name(t)?; + Type::Object { .. } => "".into(), + Type::Record { .. } => { + let record_type_name = type_name(type_, ci)?; format!( "{name}.getMap(\"{field_name}\")?.let {{ as{record_type_name}(it)}}{mandatory_suffix}" ) } - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + Type::Enum { + name: inner_name, .. + } => { + let enum_def = ci.get_enum_definition(&inner_name).unwrap(); match enum_def.is_flat() { false => { - format!("{name}.getMap(\"{field_name}\")?.let {{ as{inner}(it)}}{mandatory_suffix}") + format!("{name}.getMap(\"{field_name}\")?.let {{ as{inner_name}(it)}}{mandatory_suffix}") } true => format!( - "{name}.getString(\"{field_name}\")?.let {{ as{inner}(it)}}{mandatory_suffix}" + "{name}.getString(\"{field_name}\")?.let {{ as{inner_name}(it)}}{mandatory_suffix}" ), } } - Type::Error(_) => "".into(), - Type::CallbackInterface(_) => "".into(), - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::CallbackInterface { .. } => "".into(), + Type::ForeignExecutor { .. } => "".into(), + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); let inner_res = render_from_map(unboxed, ci, name, field_name, true)?; format!("if (hasNonNullKey({name}, \"{field_name}\")) {inner_res} else null") } - Type::Sequence(inner) => { - let unboxed = inner.as_ref(); - let element_type_name = type_name(unboxed)?; + Type::Sequence { inner_type } => { + let unboxed = inner_type.as_ref(); + let element_type_name = type_name(unboxed, ci)?; format!("{name}.getArray(\"{field_name}\")?.let {{ as{element_type_name}List(it) }}{mandatory_suffix}") } - Type::Map(_, _) => "".into(), + Type::Map { .. } => "".into(), Type::External { .. } => "".into(), Type::Custom { .. } => "".into(), - Type::Unresolved { .. } => "".into(), }; Ok(res.to_string()) } /// Get the idiomatic Kotlin rendering of a variable name. pub fn var_name(nm: &str) -> Result { - Ok(format!("`{}`", nm.to_string().to_lower_camel_case())) + Ok(oracle().var_name(nm)) } pub fn unquote(nm: &str) -> Result { @@ -270,16 +412,16 @@ pub mod filters { } pub fn rn_convert_type( - t: &TypeIdentifier, + type_: &impl AsType, _ci: &ComponentInterface, ) -> Result { - match t { + match type_.as_type() { Type::UInt8 | Type::UInt16 | Type::UInt32 => Ok(".toUInt()".to_string()), Type::Int64 => Ok(".toLong()".to_string()), Type::UInt64 => Ok(".toULong()".to_string()), Type::Float32 | Type::Float64 => Ok(".toFloat()".to_string()), - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); let conversion = rn_convert_type(unboxed, _ci).unwrap(); let optional = match *unboxed { Type::Int8 @@ -291,7 +433,7 @@ pub mod filters { Type::Int64 => ".takeUnless { it == 0L }".to_string(), Type::UInt64 => ".takeUnless { it == 0UL }".to_string(), Type::Float32 | Type::Float64 => ".takeUnless { it == 0.0 }".to_string(), - Type::String => ".takeUnless { it.isEmpty() }".to_string(), + Type::String | Type::Bytes => ".takeUnless { it.isEmpty() }".to_string(), _ => "".to_string(), }; Ok(format!("{}{}", conversion, optional)) @@ -301,29 +443,29 @@ pub mod filters { } pub fn rn_type_name( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, ) -> Result { - match t { + match type_.as_type() { Type::Boolean => Ok("Boolean".to_string()), Type::Int8 | Type::UInt8 | Type::Int16 | Type::UInt16 | Type::Int32 | Type::UInt32 => { Ok("Int".to_string()) } Type::Int64 | Type::UInt64 | Type::Float32 | Type::Float64 => Ok("Double".to_string()), Type::String => Ok("String".to_string()), - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { false => Ok("ReadableMap".to_string()), true => Ok("String".to_string()), } } - Type::Record(_) => Ok("ReadableMap".to_string()), - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::Record { .. } => Ok("ReadableMap".to_string()), + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); rn_type_name(unboxed, ci) } - Type::Sequence(_) => Ok("ReadableArray".to_string()), + Type::Sequence { .. } => Ok("ReadableArray".to_string()), _ => Ok("".to_string()), } } diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/object.rs b/lib/bindings/langs/react-native/src/gen_kotlin/object.rs new file mode 100644 index 000000000..4a7cfd6cf --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/object.rs @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use uniffi_bindgen::ComponentInterface; + +#[derive(Debug)] +pub struct ObjectCodeType { + id: String, +} + +impl ObjectCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for ObjectCodeType { + fn type_label(&self, ci: &ComponentInterface) -> String { + super::KotlinCodeOracle.class_name(ci, &self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/primitives.rs b/lib/bindings/langs/react-native/src/gen_kotlin/primitives.rs new file mode 100644 index 000000000..93c327869 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/primitives.rs @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use paste::paste; +use uniffi_bindgen::{ + backend::{Literal, Type}, + interface::Radix, + ComponentInterface, +}; + +#[allow(dead_code)] +fn render_literal(literal: &Literal, _ci: &ComponentInterface) -> String { + fn typed_number(type_: &Type, num_str: String) -> String { + match type_ { + // Bytes, Shorts and Ints can all be inferred from the type. + Type::Int8 | Type::Int16 | Type::Int32 => num_str, + Type::Int64 => format!("{num_str}L"), + + Type::UInt8 | Type::UInt16 | Type::UInt32 => format!("{num_str}u"), + Type::UInt64 => format!("{num_str}uL"), + + Type::Float32 => format!("{num_str}f"), + Type::Float64 => num_str, + _ => panic!("Unexpected literal: {num_str} is not a number"), + } + } + + match literal { + Literal::Boolean(v) => format!("{v}"), + Literal::String(s) => format!("\"{s}\""), + Literal::Int(i, radix, type_) => typed_number( + type_, + match radix { + Radix::Octal => format!("{i:#x}"), + Radix::Decimal => format!("{i}"), + Radix::Hexadecimal => format!("{i:#x}"), + }, + ), + Literal::UInt(i, radix, type_) => typed_number( + type_, + match radix { + Radix::Octal => format!("{i:#x}"), + Radix::Decimal => format!("{i}"), + Radix::Hexadecimal => format!("{i:#x}"), + }, + ), + Literal::Float(string, type_) => typed_number(type_, string.clone()), + + _ => unreachable!("Literal"), + } +} + +macro_rules! impl_code_type_for_primitive { + ($T:ty, $class_name:literal) => { + paste! { + #[derive(Debug)] + pub struct $T; + + impl CodeType for $T { + fn type_label(&self, _ci: &ComponentInterface) -> String { + $class_name.into() + } + + fn canonical_name(&self) -> String { + $class_name.into() + } + + fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String { + render_literal(&literal, ci) + } + } + } + }; +} + +impl_code_type_for_primitive!(BooleanCodeType, "Boolean"); +impl_code_type_for_primitive!(StringCodeType, "String"); +impl_code_type_for_primitive!(BytesCodeType, "ByteArray"); +impl_code_type_for_primitive!(Int8CodeType, "Byte"); +impl_code_type_for_primitive!(Int16CodeType, "Short"); +impl_code_type_for_primitive!(Int32CodeType, "Int"); +impl_code_type_for_primitive!(Int64CodeType, "Long"); +impl_code_type_for_primitive!(UInt8CodeType, "UByte"); +impl_code_type_for_primitive!(UInt16CodeType, "UShort"); +impl_code_type_for_primitive!(UInt32CodeType, "UInt"); +impl_code_type_for_primitive!(UInt64CodeType, "ULong"); +impl_code_type_for_primitive!(Float32CodeType, "Float"); +impl_code_type_for_primitive!(Float64CodeType, "Double"); diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/record.rs b/lib/bindings/langs/react-native/src/gen_kotlin/record.rs new file mode 100644 index 000000000..6d95b8b42 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_kotlin/record.rs @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use uniffi_bindgen::ComponentInterface; + +#[derive(Debug)] +pub struct RecordCodeType { + id: String, +} + +impl RecordCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for RecordCodeType { + fn type_label(&self, ci: &ComponentInterface) -> String { + super::KotlinCodeOracle.class_name(ci, &self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/EnumTemplate.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/EnumTemplate.kt index fdab66bdf..1fec22fb4 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/EnumTemplate.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/EnumTemplate.kt @@ -1,3 +1,4 @@ +{%- if !ci.is_name_used_as_error(name) -%} {%- let e = ci.get_enum_definition(name).unwrap() %} {%- if e.is_flat() %} @@ -14,7 +15,7 @@ fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_na if (type == "{{ variant.name()|var_name|unquote }}") { {% if variant.has_fields() -%} {%- for field in variant.fields() %} - val {{field.name()|var_name|unquote}} = {{ field.type_()|render_from_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }} + val {{field.name()|var_name|unquote}} = {{ field|render_from_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }} {%- endfor %} return {{ type_name }}.{{ variant.name() }}( {%- call kt::field_list(variant) -%} ) {%- else %} @@ -33,7 +34,7 @@ fun readableMapOf({{ type_name|var_name|unquote }}: {{ type_name }}): ReadableMa is {{ type_name }}.{{ variant.name() }} -> { pushToMap(map, "type", "{{ variant.name()|var_name|unquote }}") {% for f in variant.fields() -%} - pushToMap(map, "{{ f.name()|var_name|unquote }}", {{ f.type_()|render_to_map(ci,type_name|var_name|unquote,f.name()|var_name|unquote, false) }}) + pushToMap(map, "{{ f.name()|var_name|unquote }}", {{ f|render_to_map(ci, type_name|var_name|unquote, f.name()|var_name|unquote, false) }}) {% endfor -%} } {% endfor %} @@ -56,4 +57,5 @@ fun as{{ type_name }}List(arr: ReadableArray): List<{{ type_name }}> { } } return list -} \ No newline at end of file +} +{%- endif %} \ No newline at end of file diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/Objects.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/Objects.kt index c7483906f..2529548b5 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/Objects.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/Objects.kt @@ -1,7 +1,7 @@ {%- for type_ in ci.iter_types() %} -{%- let type_name = type_|type_name %} +{%- let type_name = type_|type_name(ci) %} {%- match type_ %} -{%- when Type::Object ( name ) %} +{%- when Type::Object { module_path, name, imp } %} {% let obj = ci.get_object_definition(name).unwrap() %} {% let obj_interface = "getBindingLiquidSdk()." %} {%- for func in obj.methods() -%} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/RecordTemplate.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/RecordTemplate.kt index 3d3e6bbf6..d1bb16c9d 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/RecordTemplate.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/RecordTemplate.kt @@ -2,8 +2,8 @@ fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_name }}? { if (!validateMandatoryFields({{ type_name|var_name|unquote }}, arrayOf( {%- for field in rec.fields() %} - {%- match field.type_() %} - {%- when Type::Optional(_) %} + {%- match field.as_type() %} + {%- when Type::Optional { inner_type } %} {%- else %} "{{ field.name()|var_name |unquote }}", {%- endmatch %} @@ -13,7 +13,7 @@ fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_na } {%- for field in rec.fields() %} - val {{field.name()|var_name|unquote}} = {{ field.type_()|render_from_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }} + val {{field.name()|var_name|unquote}} = {{ field|render_from_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }} {%- endfor %} return {{ type_name }}({%- call kt::field_list(rec) -%}) } @@ -21,19 +21,19 @@ fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_na fun readableMapOf({{ type_name|var_name|unquote }}: {{ type_name }}): ReadableMap { return readableMapOf( {%- for field in rec.fields() %} - {%- match field.type_() %} - {%- when Type::Optional(inner) %} - {%- let unboxed = inner.as_ref() %} + {%- match field.as_type() %} + {%- when Type::Optional { inner_type } %} + {%- let unboxed = inner_type.as_ref() %} {%- match unboxed %} - {%- when Type::Sequence(inner_type) %} - {{- self.add_sequence_type(inner_type|type_name) }} + {%- when Type::Sequence { inner_type } %} + {{- self.add_sequence_type(inner_type|type_name(ci)) }} {%- else %} {%- endmatch %} - {%- when Type::Sequence(inner_type) %} - {{- self.add_sequence_type(inner_type|type_name) }} + {%- when Type::Sequence { inner_type } %} + {{- self.add_sequence_type(inner_type|type_name(ci)) }} {%- else %} {%- endmatch %} - "{{ field.name()|var_name|unquote }}" to {{ field.type_()|render_to_map(ci,type_name|var_name|unquote, field.name()|var_name|unquote, false) }}, + "{{ field.name()|var_name|unquote }}" to {{ field|render_to_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }}, {%- endfor %} ) } diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/TopLevelFunctionTemplate.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/TopLevelFunctionTemplate.kt index d2c48162e..2f94567da 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/TopLevelFunctionTemplate.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/TopLevelFunctionTemplate.kt @@ -4,18 +4,18 @@ executor.execute { try { {%- for arg in func.arguments() -%} - {%- match arg.type_() %} - {%- when Type::Enum(inner) %} - {%- let e = ci.get_enum_definition(inner).unwrap() %} + {%- match arg.as_type() %} + {%- when Type::Enum { name, module_path } %} + {%- let e = ci.get_enum_definition(name).unwrap() %} {%- if e.is_flat() %} - val {{arg.name()|var_name|unquote|temporary}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }}) + val {{arg.name()|var_name|unquote|temporary}} = as{{arg|type_name(ci)}}({{ arg.name()|var_name|unquote }}) {%- else %} - val {{arg.name()|var_name|unquote|temporary}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }}) ?: run { throw SdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg.type_()|type_name }}")) } + val {{arg.name()|var_name|unquote|temporary}} = as{{arg|type_name(ci)}}({{ arg.name()|var_name|unquote }}) ?: run { throw SdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg|type_name(ci) }}")) } {%- endif %} - {%- when Type::Optional(_) %} - val {{arg.name()|var_name|unquote|temporary}} = {{arg.name()|var_name|unquote}}{{ arg.type_()|rn_convert_type(ci) -}} - {%- when Type::Record(_) %} - val {{arg.type_()|type_name|var_name|unquote}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }}) ?: run { throw SdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg.type_()|type_name }}")) } + {%- when Type::Optional { inner_type } %} + val {{arg.name()|var_name|unquote|temporary}} = {{arg.name()|var_name|unquote}}{{ arg|rn_convert_type(ci) -}} + {%- when Type::Record { name, module_path } %} + val {{arg|type_name(ci)|var_name|unquote}} = as{{arg|type_name(ci)}}({{ arg.name()|var_name|unquote }}) ?: run { throw SdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg|type_name(ci) }}")) } {%- else %} {%- endmatch %} {%- endfor %} @@ -28,8 +28,8 @@ res.workingDir = workingDir.absolutePath {%- endif -%} {%- match return_type %} - {%- when Type::Optional(inner) %} - {%- let unboxed = inner.as_ref() %} + {%- when Type::Optional { inner_type } %} + {%- let unboxed = inner_type.as_ref() %} promise.resolve(res?.let { {% call kt::return_value(unboxed) %} }) {%- else %} promise.resolve({% call kt::return_value(return_type) %}) diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/Types.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/Types.kt index 2985efec3..73e0e9b7b 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/Types.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/Types.kt @@ -1,25 +1,25 @@ {%- for type_ in ci.iter_types() -%} -{%- let type_name = type_|type_name -%} +{%- let type_name = type_|type_name(ci) -%} {%- match type_ -%} -{%- when Type::Record ( name ) %} +{%- when Type::Record { name, module_path } %} {%- include "RecordTemplate.kt" %} -{%- when Type::Enum ( name ) %} +{%- when Type::Enum { name, module_path } %} {%- include "EnumTemplate.kt" %} -{%- when Type::Object ( name ) %} +{%- when Type::Object { module_path, name, imp } %} {% let obj = ci.get_object_definition(name).unwrap() -%} {%- for func in obj.methods() -%} {%- match func.return_type() -%} {%- when Some with (return_type) -%} {%- match return_type -%} - {%- when Type::Optional(inner) -%} - {%- let unboxed = inner.as_ref() -%} + {%- when Type::Optional { inner_type } -%} + {%- let unboxed = inner_type.as_ref() -%} {%- match unboxed -%} - {%- when Type::Sequence(inner_type) -%} - {{- self.add_sequence_type(inner_type|type_name) -}} + {%- when Type::Sequence { inner_type } -%} + {{- self.add_sequence_type(inner_type|type_name(ci)) -}} {%- else -%} {%- endmatch -%} - {%- when Type::Sequence(inner_type) -%} - {{- self.add_sequence_type(inner_type|type_name) -}} + {%- when Type::Sequence { inner_type } -%} + {{- self.add_sequence_type(inner_type|type_name(ci)) -}} {%- else -%} {%- endmatch -%} {%- else -%} diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/macros.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/macros.kt index 41c2f3781..bd6edf6bb 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/macros.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/macros.kt @@ -1,15 +1,15 @@ {% macro arg_list(func) %} {%- for arg in func.arguments() -%} - {%- match arg.type_() -%} - {%- when Type::Enum(_) -%} + {%- match arg.as_type() -%} + {%- when Type::Enum { name, module_path } -%} {{ arg.name()|var_name|unquote|temporary }} - {%- when Type::Optional(_) -%} + {%- when Type::Optional { inner_type } -%} {{ arg.name()|var_name|unquote|temporary }} - {%- when Type::Record(_) -%} - {{ arg.type_()|type_name|var_name|unquote -}} + {%- when Type::Record { name, module_path } -%} + {{ arg|type_name(ci)|var_name|unquote -}} {%- else -%} - {{ arg.name()|var_name|unquote }}{{ arg.type_()|rn_convert_type(ci) -}} + {{ arg.name()|var_name|unquote }}{{ arg|rn_convert_type(ci) -}} {%- endmatch -%} {%- if !loop.last %}, {% endif -%} {%- endfor %} @@ -17,7 +17,7 @@ {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{- arg.name()|var_name|unquote }}: {{ arg.type_()|rn_type_name(ci) -}}, {% endfor %} + {{- arg.name()|var_name|unquote }}: {{ arg|rn_type_name(ci) -}}, {% endfor %} {%- endmacro %} {%- macro field_list(rec) %} @@ -28,9 +28,9 @@ {% macro return_value(ret_type) %} {%- match ret_type %} - {%- when Type::Enum(_) %}readableMapOf(res) - {%- when Type::Record(_) %}readableMapOf(res) - {%- when Type::Sequence(_) %}readableArrayOf(res) + {%- when Type::Enum { name, module_path } %}readableMapOf(res) + {%- when Type::Record { name, module_path } %}readableMapOf(res) + {%- when Type::Sequence { inner_type } %}readableArrayOf(res) {%- else %}res {%- endmatch %} {%- endmacro %} \ No newline at end of file diff --git a/lib/bindings/langs/react-native/src/gen_swift/callback_interface.rs b/lib/bindings/langs/react-native/src/gen_swift/callback_interface.rs new file mode 100644 index 000000000..5d8b37e0a --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/callback_interface.rs @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct CallbackInterfaceCodeType { + id: String, +} + +impl CallbackInterfaceCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for CallbackInterfaceCodeType { + fn type_label(&self) -> String { + super::SwiftCodeOracle.class_name(&self.id) + } + + fn canonical_name(&self) -> String { + format!("CallbackInterface{}", self.type_label()) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/compounds.rs b/lib/bindings/langs/react-native/src/gen_swift/compounds.rs new file mode 100644 index 000000000..9c35666bf --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/compounds.rs @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::backend::{Literal, Type}; + +use super::CodeType; + +#[derive(Debug)] +pub struct OptionalCodeType { + inner: Type, +} + +impl OptionalCodeType { + pub fn new(inner: Type) -> Self { + Self { inner } + } +} + +impl CodeType for OptionalCodeType { + fn type_label(&self) -> String { + format!("{}?", super::SwiftCodeOracle.find(&self.inner).type_label()) + } + + fn canonical_name(&self) -> String { + format!( + "Option{}", + super::SwiftCodeOracle.find(&self.inner).canonical_name() + ) + } + + fn literal(&self, literal: &Literal) -> String { + match literal { + Literal::Null => "nil".into(), + _ => super::SwiftCodeOracle.find(&self.inner).literal(literal), + } + } +} + +#[derive(Debug)] +pub struct SequenceCodeType { + inner: Type, +} + +impl SequenceCodeType { + pub fn new(inner: Type) -> Self { + Self { inner } + } +} + +impl CodeType for SequenceCodeType { + fn type_label(&self) -> String { + format!( + "[{}]", + super::SwiftCodeOracle.find(&self.inner).type_label() + ) + } + + fn canonical_name(&self) -> String { + format!( + "Sequence{}", + super::SwiftCodeOracle.find(&self.inner).canonical_name() + ) + } + + fn literal(&self, literal: &Literal) -> String { + match literal { + Literal::EmptySequence => "[]".into(), + _ => unreachable!(), + } + } +} + +#[derive(Debug)] +pub struct MapCodeType { + key: Type, + value: Type, +} + +impl MapCodeType { + pub fn new(key: Type, value: Type) -> Self { + Self { key, value } + } +} + +impl CodeType for MapCodeType { + fn type_label(&self) -> String { + format!( + "[{}: {}]", + super::SwiftCodeOracle.find(&self.key).type_label(), + super::SwiftCodeOracle.find(&self.value).type_label() + ) + } + + fn canonical_name(&self) -> String { + format!( + "Dictionary{}{}", + super::SwiftCodeOracle.find(&self.key).canonical_name(), + super::SwiftCodeOracle.find(&self.value).canonical_name() + ) + } + + fn literal(&self, literal: &Literal) -> String { + match literal { + Literal::EmptyMap => "[:]".into(), + _ => unreachable!(), + } + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/custom.rs b/lib/bindings/langs/react-native/src/gen_swift/custom.rs new file mode 100644 index 000000000..f4591b6eb --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/custom.rs @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct CustomCodeType { + name: String, +} + +impl CustomCodeType { + pub fn new(name: String) -> Self { + CustomCodeType { name } + } +} + +impl CodeType for CustomCodeType { + fn type_label(&self) -> String { + self.name.clone() + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.name) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/enum_.rs b/lib/bindings/langs/react-native/src/gen_swift/enum_.rs new file mode 100644 index 000000000..24bb927d0 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/enum_.rs @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use uniffi_bindgen::backend::Literal; + +use super::CodeType; + +#[derive(Debug)] +pub struct EnumCodeType { + id: String, +} + +impl EnumCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for EnumCodeType { + fn type_label(&self) -> String { + super::SwiftCodeOracle.class_name(&self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } + + fn literal(&self, literal: &Literal) -> String { + if let Literal::Enum(v, _) = literal { + format!(".{}", super::SwiftCodeOracle.enum_variant_name(v)) + } else { + unreachable!(); + } + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/executor.rs b/lib/bindings/langs/react-native/src/gen_swift/executor.rs new file mode 100644 index 000000000..b488b004c --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/executor.rs @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct ForeignExecutorCodeType; + +impl CodeType for ForeignExecutorCodeType { + fn type_label(&self) -> String { + // On Swift, we define a struct to represent a ForeignExecutor + "UniFfiForeignExecutor".into() + } + + fn canonical_name(&self) -> String { + "ForeignExecutor".into() + } + + fn initialization_fn(&self) -> Option { + Some("uniffiInitForeignExecutor".into()) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/external.rs b/lib/bindings/langs/react-native/src/gen_swift/external.rs new file mode 100644 index 000000000..0b6728ba8 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/external.rs @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct ExternalCodeType { + name: String, +} + +impl ExternalCodeType { + pub fn new(name: String) -> Self { + ExternalCodeType { name } + } +} + +impl CodeType for ExternalCodeType { + fn type_label(&self) -> String { + self.name.clone() + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.name) + } + + // lower and lift need to call public function which were generated for + // the original types. + fn lower(&self) -> String { + format!("{}_lower", self.ffi_converter_name()) + } + + fn lift(&self) -> String { + format!("{}_lift", self.ffi_converter_name()) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/miscellany.rs b/lib/bindings/langs/react-native/src/gen_swift/miscellany.rs new file mode 100644 index 000000000..c45091c80 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/miscellany.rs @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct TimestampCodeType; + +impl CodeType for TimestampCodeType { + fn type_label(&self) -> String { + "Date".into() + } + + fn canonical_name(&self) -> String { + "Timestamp".into() + } +} + +#[derive(Debug)] +pub struct DurationCodeType; + +impl CodeType for DurationCodeType { + fn type_label(&self) -> String { + "TimeInterval".into() + } + + fn canonical_name(&self) -> String { + "Duration".into() + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/mod.rs b/lib/bindings/langs/react-native/src/gen_swift/mod.rs index 7527c9266..9141c01bc 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_swift/mod.rs @@ -1,12 +1,83 @@ use std::collections::HashSet; +use std::fmt::Debug; use askama::Template; +use heck::{ToLowerCamelCase, ToUpperCamelCase}; use once_cell::sync::Lazy; use uniffi_bindgen::interface::*; -use crate::generator::RNConfig; +use crate::generator::Config; -pub use uniffi_bindgen::bindings::swift::gen_swift::*; +mod callback_interface; +mod compounds; +mod custom; +mod enum_; +mod executor; +mod external; +mod miscellany; +mod object; +mod primitives; +mod record; + +/// A trait tor the implementation. +#[allow(dead_code)] +trait CodeType: Debug { + /// The language specific label used to reference this type. This will be used in + /// method signatures and property declarations. + fn type_label(&self) -> String; + + /// A representation of this type label that can be used as part of another + /// identifier. e.g. `read_foo()`, or `FooInternals`. + /// + /// This is especially useful when creating specialized objects or methods to deal + /// with this type only. + fn canonical_name(&self) -> String { + self.type_label() + } + + fn literal(&self, _literal: &Literal) -> String { + unimplemented!("Unimplemented for {}", self.type_label()) + } + + /// Name of the FfiConverter + /// + /// This is the object that contains the lower, write, lift, and read methods for this type. + fn ffi_converter_name(&self) -> String { + format!("FfiConverter{}", self.canonical_name()) + } + + // XXX - the below should be removed and replace with the ffi_converter_name reference in the template. + /// An expression for lowering a value into something we can pass over the FFI. + fn lower(&self) -> String { + format!("{}.lower", self.ffi_converter_name()) + } + + /// An expression for writing a value into a byte buffer. + fn write(&self) -> String { + format!("{}.write", self.ffi_converter_name()) + } + + /// An expression for lifting a value from something we received over the FFI. + fn lift(&self) -> String { + format!("{}.lift", self.ffi_converter_name()) + } + + /// An expression for reading a value from a byte buffer. + fn read(&self) -> String { + format!("{}.read", self.ffi_converter_name()) + } + + /// A list of imports that are needed if this type is in use. + /// Classes are imported exactly once. + fn imports(&self) -> Option> { + None + } + + /// Function to run at startup + fn initialization_fn(&self) -> Option { + None + } +} static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { let list: Vec<&str> = vec!["connect", "add_event_listener", "set_logger"]; @@ -17,12 +88,12 @@ static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { #[template(syntax = "rn", escape = "none", path = "mapper.swift")] #[allow(dead_code)] pub struct MapperGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, } impl<'a> MapperGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci } } } @@ -31,12 +102,12 @@ impl<'a> MapperGenerator<'a> { #[template(syntax = "rn", escape = "none", path = "extern.m")] #[allow(dead_code)] pub struct ExternGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, } impl<'a> ExternGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci } } } @@ -45,30 +116,103 @@ impl<'a> ExternGenerator<'a> { #[template(syntax = "rn", escape = "none", path = "module.swift")] #[allow(dead_code)] pub struct ModuleGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, } impl<'a> ModuleGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci } } } -pub mod filters { +#[derive(Clone)] +pub struct SwiftCodeOracle; + +#[allow(dead_code)] +impl SwiftCodeOracle { + // Map `Type` instances to a `Box` for that type. + // + // There is a companion match in `templates/Types.swift` which performs a similar function for the + // template code. + // + // - When adding additional types here, make sure to also add a match arm to the `Types.swift` template. + // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches + fn create_code_type(&self, type_: Type) -> Box { + match type_ { + Type::UInt8 => Box::new(primitives::UInt8CodeType), + Type::Int8 => Box::new(primitives::Int8CodeType), + Type::UInt16 => Box::new(primitives::UInt16CodeType), + Type::Int16 => Box::new(primitives::Int16CodeType), + Type::UInt32 => Box::new(primitives::UInt32CodeType), + Type::Int32 => Box::new(primitives::Int32CodeType), + Type::UInt64 => Box::new(primitives::UInt64CodeType), + Type::Int64 => Box::new(primitives::Int64CodeType), + Type::Float32 => Box::new(primitives::Float32CodeType), + Type::Float64 => Box::new(primitives::Float64CodeType), + Type::Boolean => Box::new(primitives::BooleanCodeType), + Type::String => Box::new(primitives::StringCodeType), + Type::Bytes => Box::new(primitives::BytesCodeType), - use heck::*; - use uniffi_bindgen::backend::CodeOracle; - use uniffi_bindgen::backend::{CodeType, TypeIdentifier}; + Type::Timestamp => Box::new(miscellany::TimestampCodeType), + Type::Duration => Box::new(miscellany::DurationCodeType), + Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)), + Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)), + Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)), + Type::CallbackInterface { name, .. } => { + Box::new(callback_interface::CallbackInterfaceCodeType::new(name)) + } + Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType), + Type::Optional { inner_type } => { + Box::new(compounds::OptionalCodeType::new(*inner_type)) + } + Type::Sequence { inner_type } => { + Box::new(compounds::SequenceCodeType::new(*inner_type)) + } + Type::Map { + key_type, + value_type, + } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)), + Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)), + Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)), + } + } + + fn find(&self, type_: &impl AsType) -> Box { + self.create_code_type(type_.as_type()) + } + + /// Get the idiomatic Swift rendering of a class name (for enums, records, errors, etc). + fn class_name(&self, nm: &str) -> String { + nm.to_string().to_upper_camel_case() + } + + /// Get the idiomatic Swift rendering of a function name. + fn fn_name(&self, nm: &str) -> String { + nm.to_string().to_lower_camel_case() + } + + /// Get the idiomatic Swift rendering of a variable name. + fn var_name(&self, nm: &str) -> String { + nm.to_string().to_lower_camel_case() + } + + /// Get the idiomatic Swift rendering of an individual enum variant. + fn enum_variant_name(&self, nm: &str) -> String { + nm.to_string().to_lower_camel_case() + } +} + +pub mod filters { use super::*; fn oracle() -> &'static SwiftCodeOracle { &SwiftCodeOracle } - pub fn type_name(codetype: &impl CodeType) -> Result { - Ok(codetype.type_label(oracle())) + pub fn type_name(as_type: &impl AsType) -> Result { + Ok(oracle().find(as_type).type_label()) } pub fn fn_name(nm: &str) -> Result { @@ -76,13 +220,13 @@ pub mod filters { } pub fn render_to_map( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, obj_name: &str, field_name: &str, optional: bool, ) -> Result { - let type_name = filters::type_name(t)?; + let type_name = filters::type_name(type_)?; let type_name_str = type_name.as_str(); let var_name = filters::unquote(filters::var_name(type_name_str)?.as_str())?; let mut obj_prefix = "".to_string(); @@ -93,7 +237,7 @@ pub mod filters { if optional { optional_suffix = "!"; } - let res: Result = match t { + let res: Result = match type_.as_type() { Type::UInt8 => Ok(format!("{obj_prefix}{field_name}")), Type::Int8 => Ok(format!("{obj_prefix}{field_name}")), Type::UInt16 => Ok(format!("{obj_prefix}{field_name}")), @@ -106,14 +250,15 @@ pub mod filters { Type::Float64 => Ok(format!("{obj_prefix}{field_name}")), Type::Boolean => Ok(format!("{obj_prefix}{field_name}")), Type::String => Ok(format!("{obj_prefix}{field_name}")), + Type::Bytes => Ok(format!("{obj_prefix}{field_name}")), Type::Timestamp => unimplemented!("render_to_map: Timestamp is not implemented"), Type::Duration => unimplemented!("render_to_map: Duration is not implemented"), - Type::Object(_) => unimplemented!("render_to_map: Object is not implemented"), - Type::Record(_) => Ok(format!( + Type::Object { .. } => unimplemented!("render_to_map: Object is not implemented"), + Type::Record { .. } => Ok(format!( "dictionaryOf({var_name}: {obj_prefix}{field_name}{optional_suffix})" )), - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { true => Ok(format!( "valueOf( {var_name}: {obj_prefix}{field_name}{optional_suffix})" @@ -123,54 +268,53 @@ pub mod filters { )), } } - Type::Error(_) => unimplemented!("render_to_map: Error is not implemented"), - Type::CallbackInterface(_) => { + Type::CallbackInterface { .. } => { unimplemented!("render_to_map: CallbackInterface is not implemented") } - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::ForeignExecutor { .. } => { + unimplemented!("render_to_map: ForeignExecutor is not implemented") + } + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); let inner_render = render_to_map(unboxed, ci, obj_name, field_name, true)?; Ok(format!( "{obj_prefix}{field_name} == nil ? nil : {inner_render}" )) } - Type::Sequence(inner) => { - let unboxed = inner.as_ref(); + Type::Sequence { inner_type } => { + let unboxed = inner_type.as_ref(); let type_name = filters::type_name(unboxed)?; let var_name = filters::var_name(type_name.as_str())?; let var_name = filters::unquote(var_name.as_str())?; let as_array_statment = match unboxed { - Type::Record(_) => format!( + Type::Record { .. } => format!( "arrayOf({var_name}List: {obj_prefix}{field_name}{optional_suffix})" ), - Type::Enum(_) => format!( + Type::Enum { .. } => format!( "arrayOf({var_name}List: {obj_prefix}{field_name}{optional_suffix})" ), _ => format!("{obj_prefix}{field_name}"), }; Ok(as_array_statment) } - Type::Map(_, _) => unimplemented!("render_to_map: Map is not implemented"), + Type::Map { .. } => unimplemented!("render_to_map: Map is not implemented"), Type::External { .. } => { unimplemented!("render_to_map: External is not implemented") } Type::Custom { .. } => { unimplemented!("render_to_map: Custom is not implemented") } - Type::Unresolved { .. } => { - unimplemented!("render_to_map: Unresolved is not implemented") - } }; res } pub fn rn_convert_type( - t: &TypeIdentifier, + type_: &impl AsType, converted_var_name: &str, ) -> Result { - match t { - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + match type_.as_type() { + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); let optional = match *unboxed { Type::Int8 | Type::UInt8 @@ -190,6 +334,10 @@ pub mod filters { "{}.isEmpty ? nil : {}", converted_var_name, converted_var_name ), + Type::Bytes => format!( + "{}.isEmpty ? nil : {}", + converted_var_name, converted_var_name + ), _ => "".to_string(), }; Ok(optional.to_string()) @@ -199,7 +347,7 @@ pub mod filters { } pub fn rn_return_type( - t: &TypeIdentifier, + type_: &impl AsType, name: &str, optional: bool, ) -> Result { @@ -207,15 +355,15 @@ pub mod filters { if optional { optional_suffix = "!"; } - match t { - Type::Enum(_) | Type::Record(_) => Ok(format!( + match type_.as_type() { + Type::Enum { .. } | Type::Record { .. } => Ok(format!( "BreezSDKLiquidMapper.dictionaryOf({}: res{})", name, optional_suffix )), - Type::Sequence(inner) => { - let unboxed = inner.as_ref(); + Type::Sequence { inner_type } => { + let unboxed = inner_type.as_ref(); match unboxed { - Type::Enum(_) | Type::Record(_) => Ok(format!( + Type::Enum { .. } | Type::Record { .. } => Ok(format!( "BreezSDKLiquidMapper.arrayOf({}List: res{})", name, optional_suffix )), @@ -227,7 +375,7 @@ pub mod filters { } pub fn rn_type_name( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, optional: bool, ) -> Result { @@ -235,35 +383,35 @@ pub mod filters { if optional { optional_suffix = "?"; } - match t { - Type::Record(_) => Ok(format!("[String: Any{}]", optional_suffix)), - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + match type_.as_type() { + Type::Record { .. } => Ok(format!("[String: Any{}]", optional_suffix)), + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { false => Ok(format!("[String: Any{}]", optional_suffix)), true => Ok("String".into()), } } - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); rn_type_name(unboxed, ci, optional) } - Type::Sequence(inner) => { - let unboxed = inner.as_ref(); + Type::Sequence { inner_type } => { + let unboxed = inner_type.as_ref(); Ok(format!("[{}]", rn_type_name(unboxed, ci, optional)?)) } - t => { - let name = filters::type_name(t)?; + _ => { + let name = filters::type_name(type_)?; Ok(name.to_string()) } } } pub fn extern_type_name( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, ) -> Result { - match t { + match type_.as_type() { Type::Boolean => Ok("BOOL".to_string()), Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => Ok("NSInteger*".to_string()), Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => { @@ -271,46 +419,46 @@ pub mod filters { } Type::Float32 | Type::Float64 => Ok("NSNumber*".to_string()), Type::String => Ok("NSString*".to_string()), - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); match enum_def.is_flat() { false => Ok("NSDictionary*".to_string()), true => Ok("NSString*".to_string()), } } - Type::Record(_) => Ok("NSDictionary*".to_string()), - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::Record { .. } => Ok("NSDictionary*".to_string()), + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); extern_type_name(unboxed, ci) } - Type::Sequence(_) => Ok("NSArray*".to_string()), + Type::Bytes { .. } | Type::Sequence { .. } => Ok("NSArray*".to_string()), _ => Ok("".to_string()), } } pub fn inline_optional_field( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, ) -> Result { - match t { - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + match type_.as_type() { + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); inline_optional_field(unboxed, ci) } _ => { - let mapped_name = filters::rn_type_name(t, ci, true)?; - let type_name = filters::type_name(t)?; + let mapped_name = filters::rn_type_name(type_, ci, true)?; + let type_name = filters::type_name(type_)?; Ok(mapped_name == type_name) } } } pub fn render_from_map( - t: &TypeIdentifier, + type_: &impl AsType, ci: &ComponentInterface, map_var_name: &str, ) -> Result { - let res: String = match t { + let res: String = match type_.as_type() { Type::UInt8 => map_var_name.to_string(), Type::Int8 => map_var_name.to_string(), Type::UInt16 => map_var_name.to_string(), @@ -323,51 +471,51 @@ pub mod filters { Type::Float64 => map_var_name.to_string(), Type::Boolean => map_var_name.to_string(), Type::String => map_var_name.to_string(), + Type::Bytes => map_var_name.to_string(), Type::Timestamp => "".into(), Type::Duration => "".into(), - Type::Object(_) => "".into(), - Type::Record(_) => { - let record_type_name = type_name(t)?; + Type::Object { .. } => "".into(), + Type::Record { .. } => { + let record_type_name = type_name(type_)?; let record_var_name = var_name(&record_type_name)?; let record_unquoted_name = unquote(&record_var_name)?; format!("try as{record_type_name}({record_unquoted_name}: {map_var_name})") } - Type::Enum(inner) => { - let enum_def = ci.get_enum_definition(inner).unwrap(); - let enum_var_name = var_name(inner)?; + Type::Enum { name, .. } => { + let enum_def = ci.get_enum_definition(&name).unwrap(); + let enum_var_name = var_name(&name)?; let enum_unquoted_name = unquote(&enum_var_name)?; match enum_def.is_flat() { - false => format!("try as{inner}({enum_unquoted_name}: {map_var_name})"), - true => format!("try as{inner}({enum_unquoted_name}: {map_var_name})"), + false => format!("try as{name}({enum_unquoted_name}: {map_var_name})"), + true => format!("try as{name}({enum_unquoted_name}: {map_var_name})"), } } - Type::Error(_) => "".into(), - Type::CallbackInterface(_) => "".into(), - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + Type::CallbackInterface { .. } => "".into(), + Type::ForeignExecutor { .. } => "".into(), + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); render_from_map(unboxed, ci, map_var_name)? } - Type::Sequence(inner) => { - let unboxed = inner.as_ref(); + Type::Sequence { inner_type } => { + let unboxed = inner_type.as_ref(); let element_type_name = type_name(unboxed)?; match unboxed { - Type::Enum(_) | Type::Record(_) => { + Type::Enum { .. } | Type::Record { .. } => { format!("try as{element_type_name}List(arr: {map_var_name})") } _ => map_var_name.to_string(), } } - Type::Map(_, _) => "".into(), + Type::Map { .. } => "".into(), Type::External { .. } => "".into(), Type::Custom { .. } => "".into(), - Type::Unresolved { .. } => "".into(), }; Ok(res.to_string()) } pub fn var_name(nm: &str) -> Result { - Ok(format!("`{}`", nm.to_string().to_lower_camel_case())) + Ok(oracle().var_name(nm)) } pub fn unquote(nm: &str) -> Result { diff --git a/lib/bindings/langs/react-native/src/gen_swift/object.rs b/lib/bindings/langs/react-native/src/gen_swift/object.rs new file mode 100644 index 000000000..ea140c998 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/object.rs @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct ObjectCodeType { + id: String, +} + +impl ObjectCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for ObjectCodeType { + fn type_label(&self) -> String { + super::SwiftCodeOracle.class_name(&self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/primitives.rs b/lib/bindings/langs/react-native/src/gen_swift/primitives.rs new file mode 100644 index 000000000..f23b06c2f --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/primitives.rs @@ -0,0 +1,94 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; +use paste::paste; +use uniffi_bindgen::{ + backend::{Literal, Type}, + interface::Radix, +}; + +#[allow(dead_code)] +fn render_literal(literal: &Literal) -> String { + fn typed_number(type_: &Type, num_str: String) -> String { + match type_ { + // special case Int32. + Type::Int32 => num_str, + // otherwise use constructor e.g. UInt8(x) + Type::Int8 + | Type::UInt8 + | Type::Int16 + | Type::UInt16 + | Type::UInt32 + | Type::Int64 + | Type::UInt64 + | Type::Float32 + | Type::Float64 => + // XXX we should pass in the codetype itself. + { + format!( + "{}({num_str})", + super::SwiftCodeOracle.find(type_).type_label() + ) + } + _ => panic!("Unexpected literal: {num_str} is not a number"), + } + } + + match literal { + Literal::Boolean(v) => format!("{v}"), + Literal::String(s) => format!("\"{s}\""), + Literal::Int(i, radix, type_) => typed_number( + type_, + match radix { + Radix::Octal => format!("0o{i:o}"), + Radix::Decimal => format!("{i}"), + Radix::Hexadecimal => format!("{i:#x}"), + }, + ), + Literal::UInt(i, radix, type_) => typed_number( + type_, + match radix { + Radix::Octal => format!("0o{i:o}"), + Radix::Decimal => format!("{i}"), + Radix::Hexadecimal => format!("{i:#x}"), + }, + ), + Literal::Float(string, type_) => typed_number(type_, string.clone()), + _ => unreachable!("Literal"), + } +} + +macro_rules! impl_code_type_for_primitive { + ($T:ty, $class_name:literal) => { + paste! { + #[derive(Debug)] + pub struct $T; + + impl CodeType for $T { + fn type_label(&self) -> String { + $class_name.into() + } + + fn literal(&self, literal: &Literal) -> String { + render_literal(&literal) + } + } + } + }; +} + +impl_code_type_for_primitive!(BooleanCodeType, "Bool"); +impl_code_type_for_primitive!(StringCodeType, "String"); +impl_code_type_for_primitive!(BytesCodeType, "Data"); +impl_code_type_for_primitive!(Int8CodeType, "Int8"); +impl_code_type_for_primitive!(Int16CodeType, "Int16"); +impl_code_type_for_primitive!(Int32CodeType, "Int32"); +impl_code_type_for_primitive!(Int64CodeType, "Int64"); +impl_code_type_for_primitive!(UInt8CodeType, "UInt8"); +impl_code_type_for_primitive!(UInt16CodeType, "UInt16"); +impl_code_type_for_primitive!(UInt32CodeType, "UInt32"); +impl_code_type_for_primitive!(UInt64CodeType, "UInt64"); +impl_code_type_for_primitive!(Float32CodeType, "Float"); +impl_code_type_for_primitive!(Float64CodeType, "Double"); diff --git a/lib/bindings/langs/react-native/src/gen_swift/record.rs b/lib/bindings/langs/react-native/src/gen_swift/record.rs new file mode 100644 index 000000000..401109011 --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_swift/record.rs @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct RecordCodeType { + id: String, +} + +impl RecordCodeType { + pub fn new(id: String) -> Self { + Self { id } + } +} + +impl CodeType for RecordCodeType { + fn type_label(&self) -> String { + super::SwiftCodeOracle.class_name(&self.id) + } + + fn canonical_name(&self) -> String { + format!("Type{}", self.id) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/EnumTemplate.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/EnumTemplate.swift index 49eefeefd..bf6b593b6 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/EnumTemplate.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/EnumTemplate.swift @@ -1,3 +1,4 @@ +{%- if !ci.is_name_used_as_error(name) -%} {%- let e = ci.get_enum_definition(name).unwrap() %} {%- if e.is_flat() %} @@ -39,26 +40,26 @@ static func as{{ type_name }}({{ type_name|var_name|unquote }}: [String: Any?]) if (type == "{{ variant.name()|var_name|unquote }}") { {%- if variant.has_fields() %} {%- for field in variant.fields() %} - {%- match field.type_() %} - {%- when Type::Optional(_) %} - {% if field.type_()|inline_optional_field(ci) -%} - let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} + {%- match field.as_type() %} + {%- when Type::Optional { inner_type } %} + {% if field|inline_optional_field(ci) -%} + let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} {% else -%} - var _{{field.name()|var_name|unquote}}: {{field.type_()|type_name}} - if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} { - _{{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}} + var _{{field.name()|var_name|unquote}}: {{field|type_name}} + if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} { + _{{field.name()|var_name|unquote}} = {{field|render_from_map(ci, field.name()|var_name|unquote|temporary)}} } {% endif -%} {%- else %} - {% if field.type_()|inline_optional_field(ci) -%} - guard let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else { + {% if field|inline_optional_field(ci) -%} + guard let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}")) } {%- else -%} - guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else { + guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}")) } - let _{{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}} + let _{{field.name()|var_name|unquote}} = {{field|render_from_map(ci, field.name()|var_name|unquote|temporary)}} {% endif -%} {% endmatch %} {%- endfor %} @@ -85,7 +86,7 @@ static func dictionaryOf({{ type_name|var_name|unquote }}: {{ type_name }}) -> [ return [ "type": "{{ variant.name()|var_name|unquote }}", {%- for f in variant.fields() %} - "{{ f.name()|var_name|unquote }}": {{ f.type_()|render_to_map(ci,"",f.name()|var_name|unquote, false) }}, + "{{ f.name()|var_name|unquote }}": {{ f|render_to_map(ci,"",f.name()|var_name|unquote, false) }}, {%- endfor %} ] {%- endfor %} @@ -114,3 +115,4 @@ static func as{{ type_name }}List(arr: [Any]) throws -> [{{ type_name }}] { } return list } +{%- endif %} diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/ExternFunctionTemplate.m b/lib/bindings/langs/react-native/src/gen_swift/templates/ExternFunctionTemplate.m index 30f00a2f7..7a3109486 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/ExternFunctionTemplate.m +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/ExternFunctionTemplate.m @@ -5,9 +5,9 @@ {% else -%} {%- for arg in func.arguments() %} {%- if loop.first %} - {{ func.name()|fn_name|unquote }}: ({{arg.type_()|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }} + {{ func.name()|fn_name|unquote }}: ({{arg|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }} {%- else %} - {{ arg.name()|var_name|unquote }}: ({{arg.type_()|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }} + {{ arg.name()|var_name|unquote }}: ({{arg|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }} {%- endif -%} {% endfor %} resolve: (RCTPromiseResolveBlock)resolve diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/Objects.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/Objects.swift index 29228a463..1b537bbdd 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/Objects.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/Objects.swift @@ -1,7 +1,7 @@ {%- for type_ in ci.iter_types() %} {%- let type_name = type_|type_name %} {%- match type_ %} -{%- when Type::Object ( name ) %} +{%- when Type::Object { name, module_path, imp } %} {% let obj = ci.get_object_definition(name).unwrap() %} {% let obj_interface = "getBindingLiquidSdk()." %} {%- for func in obj.methods() -%} diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/RecordTemplate.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/RecordTemplate.swift index 1daaebbf4..1c0df45d9 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/RecordTemplate.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/RecordTemplate.swift @@ -1,31 +1,31 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} static func as{{ type_name }}({{ type_name|var_name|unquote }}: [String: Any?]) throws -> {{ type_name }} { {%- for field in rec.fields() %} - {%- match field.type_() %} - {%- when Type::Optional(_) %} - var {{field.name()|var_name|unquote}}: {{field.type_()|type_name}} - {% if field.type_()|inline_optional_field(ci) -%} + {%- match field.as_type() %} + {%- when Type::Optional { inner_type } %} + var {{field.name()|var_name|unquote}}: {{field|type_name}} + {% if field|inline_optional_field(ci) -%} if hasNonNilKey(data: {{ type_name|var_name|unquote }}, key: "{{field.name()|var_name|unquote}}") { - guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else { + guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} else { throw SdkError.Generic(message: errUnexpectedValue(fieldName: "{{field.name()|var_name|unquote}}")) } {{field.name()|var_name|unquote}} = {{field.name()|var_name|unquote|temporary}} } {%- else -%} - if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} { - {{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}} + if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} { + {{field.name()|var_name|unquote}} = {{field|render_from_map(ci, field.name()|var_name|unquote|temporary)}} } {% endif -%} {%- else %} - {% if field.type_()|inline_optional_field(ci) -%} - guard let {{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else { + {% if field|inline_optional_field(ci) -%} + guard let {{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}")) } {%- else -%} - guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else { + guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field|rn_type_name(ci, true)}} else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}")) } - let {{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}} + let {{field.name()|var_name|unquote}} = {{field|render_from_map(ci, field.name()|var_name|unquote|temporary)}} {% endif -%} {% endmatch %} {%- endfor %} @@ -36,7 +36,7 @@ static func as{{ type_name }}({{ type_name|var_name|unquote }}: [String: Any?]) static func dictionaryOf({{ type_name|var_name|unquote }}: {{ type_name }}) -> [String: Any?] { return [ {%- for field in rec.fields() %} - "{{ field.name()|var_name|unquote }}": {{ field.type_()|render_to_map(ci,type_name|var_name|unquote,field.name()|var_name|unquote,false)}}, + "{{ field.name()|var_name|unquote }}": {{ field|render_to_map(ci,type_name|var_name|unquote,field.name()|var_name|unquote,false)}}, {%- endfor %} ] } diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/TopLevelFunctionTemplate.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/TopLevelFunctionTemplate.swift index 9c52bda22..b75833475 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/TopLevelFunctionTemplate.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/TopLevelFunctionTemplate.swift @@ -3,18 +3,18 @@ func {{ func.name()|fn_name|unquote }}(_ {% call swift::arg_list_decl(func) -%}resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { do { {%- for arg in func.arguments() -%} - {%- match arg.type_() %} - {%- when Type::Enum(inner) %} - {%- let e = ci.get_enum_definition(inner).unwrap() %} + {%- match arg.as_type() %} + {%- when Type::Enum { name, module_path } %} + {%- let e = ci.get_enum_definition(name).unwrap() %} {%- if e.is_flat() %} - let {{arg.name()|var_name|unquote|temporary}} = try BreezSDKLiquidMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) + let {{arg.name()|var_name|unquote|temporary}} = try BreezSDKLiquidMapper.as{{arg|type_name}}({{ arg|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) {%- else %} - let {{arg.name()|var_name|unquote|temporary}} = try BreezSDKLiquidMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) + let {{arg.name()|var_name|unquote|temporary}} = try BreezSDKLiquidMapper.as{{arg|type_name}}({{ arg|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) {%- endif %} - {%- when Type::Optional(_) %} - let {{arg.name()|var_name|unquote|temporary}} = {{ arg.type_()|rn_convert_type(arg.name()|var_name|unquote) -}} - {%- when Type::Record(_) %} - let {{arg.type_()|type_name|var_name|unquote}} = try BreezSDKLiquidMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) + {%- when Type::Optional { inner_type } %} + let {{arg.name()|var_name|unquote|temporary}} = {{ arg|rn_convert_type(arg.name()|var_name|unquote) -}} + {%- when Type::Record { name, module_path } %} + let {{arg|type_name|var_name|unquote}} = try BreezSDKLiquidMapper.as{{arg|type_name}}({{ arg|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }}) {%- else %} {%- endmatch %} {%- endfor %} @@ -25,8 +25,8 @@ res.workingDir = RNBreezSDKLiquid.breezSdkLiquidDirectory.path {%- endif -%} {%- match return_type %} - {%- when Type::Optional(inner) %} - {%- let unboxed = inner.as_ref() %} + {%- when Type::Optional { inner_type } %} + {%- let unboxed = inner_type.as_ref() %} if res != nil { resolve({{ unboxed|rn_return_type(unboxed|type_name|var_name|unquote, true) }}) } else { diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/Types.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/Types.swift index d82cceb69..1faf0d58b 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/Types.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/Types.swift @@ -1,9 +1,9 @@ {%- for type_ in ci.iter_types() %} {%- let type_name = type_|type_name %} {%- match type_ %} -{%- when Type::Record ( name ) %} +{%- when Type::Record { name, module_path } %} {%- include "RecordTemplate.swift" %} -{%- when Type::Enum ( name ) %} +{%- when Type::Enum { name, module_path } %} {%- include "EnumTemplate.swift" %} {%- else %} {%- endmatch -%} diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m index 2de94d79d..c8254954a 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m @@ -25,7 +25,7 @@ @interface RCT_EXTERN_MODULE(RNBreezSDKLiquid, RCTEventEmitter) {%- for type_ in ci.iter_types() %} {%- let type_name = type_|type_name %} {%- match type_ %} -{%- when Type::Object ( name ) %} +{%- when Type::Object { name, module_path, imp } %} {% let obj = ci.get_object_definition(name).unwrap() %} {%- for func in obj.methods() -%} {%- if func.name()|ignored_function == false -%} diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/macros.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/macros.swift index 795d957dd..bcc962c60 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/macros.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/macros.swift @@ -1,12 +1,12 @@ {% macro arg_list(func) %} {%- for arg in func.arguments() -%} - {%- match arg.type_() -%} - {%- when Type::Enum(_) -%} + {%- match arg.as_type() -%} + {%- when Type::Enum { name, module_path } -%} {{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote|temporary -}} - {%- when Type::Optional(_) -%} + {%- when Type::Optional { inner_type } -%} {{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote|temporary -}} - {%- when Type::Record(_) -%} - {{ arg.name()|var_name|unquote }}: {{ arg.type_()|type_name|var_name|unquote -}} + {%- when Type::Record { name, module_path } -%} + {{ arg.name()|var_name|unquote }}: {{ arg|type_name|var_name|unquote -}} {%- else -%} {{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote -}} {%- endmatch -%} @@ -16,7 +16,7 @@ {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{- arg.name()|var_name|unquote }}: {{ arg.type_()|rn_type_name(ci, false) -}}, {% endfor %} + {{- arg.name()|var_name|unquote }}: {{ arg|rn_type_name(ci, false) -}}, {% endfor %} {%- endmacro %} {% macro extern_arg_list(func) %} diff --git a/lib/bindings/langs/react-native/src/gen_typescript/callback_interface.rs b/lib/bindings/langs/react-native/src/gen_typescript/callback_interface.rs index e3cb462de..e036eb00e 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/callback_interface.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/callback_interface.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; +use uniffi_bindgen::backend::{CodeType, Literal}; +#[derive(Debug)] pub struct CallbackInterfaceCodeType { id: String, } @@ -15,19 +16,15 @@ impl CallbackInterfaceCodeType { } impl CodeType for CallbackInterfaceCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - oracle.class_name(&self.id) + fn type_label(&self) -> String { + super::TypescriptCodeOracle.class_name(&self.id) } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.id) } - fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String { + fn literal(&self, _literal: &Literal) -> String { unreachable!(); } - - fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String { - nm.to_string() - } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/compounds.rs b/lib/bindings/langs/react-native/src/gen_typescript/compounds.rs index 8ce2037a8..8892d0730 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/compounds.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/compounds.rs @@ -3,46 +3,47 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use paste::paste; -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal, TypeIdentifier}; +use uniffi_bindgen::backend::{CodeType, Literal, Type}; -fn render_literal(oracle: &dyn CodeOracle, literal: &Literal, inner: &TypeIdentifier) -> String { +fn render_literal(literal: &Literal, inner: &Type) -> String { match literal { Literal::Null => "null".into(), Literal::EmptySequence => "[]".into(), Literal::EmptyMap => "{}".into(), // For optionals - _ => oracle.find(inner).literal(oracle, literal), + _ => super::TypescriptCodeOracle.find(inner).literal(literal), } } macro_rules! impl_code_type_for_compound { ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => { paste! { + #[derive(Debug)] pub struct $T { - inner: TypeIdentifier, + inner: Type, } impl $T { - pub fn new(inner: TypeIdentifier) -> Self { + pub fn new(inner: Type) -> Self { Self { inner } } - fn inner(&self) -> &TypeIdentifier { + fn inner(&self) -> &Type { &self.inner } } impl CodeType for $T { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - format!($type_label_pattern, oracle.find(self.inner()).type_label(oracle)) + fn type_label(&self) -> String { + format!($type_label_pattern, super::TypescriptCodeOracle.find(self.inner()).type_label()) } - fn canonical_name(&self, oracle: &dyn CodeOracle) -> String { - format!($canonical_name_pattern, oracle.find(self.inner()).canonical_name(oracle)) + fn canonical_name(&self) -> String { + format!($canonical_name_pattern, super::TypescriptCodeOracle.find(self.inner()).canonical_name()) } - fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String { - render_literal(oracle, literal, self.inner()) + fn literal(&self, literal: &Literal) -> String { + render_literal(literal, self.inner()) } } } @@ -52,43 +53,44 @@ macro_rules! impl_code_type_for_compound { impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}"); impl_code_type_for_compound!(SequenceCodeType, "{}[]", "Sequence{}"); +#[derive(Debug)] pub struct MapCodeType { - key: TypeIdentifier, - value: TypeIdentifier, + key: Type, + value: Type, } impl MapCodeType { - pub fn new(key: TypeIdentifier, value: TypeIdentifier) -> Self { + pub fn new(key: Type, value: Type) -> Self { Self { key, value } } - fn key(&self) -> &TypeIdentifier { + fn key(&self) -> &Type { &self.key } - fn value(&self) -> &TypeIdentifier { + fn value(&self) -> &Type { &self.value } } impl CodeType for MapCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { + fn type_label(&self) -> String { format!( "Record<{}, {}>", - self.key().type_label(oracle), - self.value().type_label(oracle), + super::TypescriptCodeOracle.find(&self.key()).type_label(), + super::TypescriptCodeOracle.find(&self.value()).type_label(), ) } - fn canonical_name(&self, oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!( "Record{}{}", - self.key().type_label(oracle), - self.value().type_label(oracle), + super::TypescriptCodeOracle.find(&self.key()).type_label(), + super::TypescriptCodeOracle.find(&self.value()).type_label(), ) } - fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String { - render_literal(oracle, literal, &self.value) + fn literal(&self, literal: &Literal) -> String { + render_literal(literal, &self.value) } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/custom.rs b/lib/bindings/langs/react-native/src/gen_typescript/custom.rs index b5209d0de..795f25f17 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/custom.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/custom.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType}; +use uniffi_bindgen::backend::CodeType; +#[derive(Debug)] pub struct CustomCodeType { name: String, } @@ -15,15 +16,11 @@ impl CustomCodeType { } impl CodeType for CustomCodeType { - fn type_label(&self, _oracle: &dyn CodeOracle) -> String { + fn type_label(&self) -> String { self.name.clone() } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.name) } - - fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String { - nm.to_string() - } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/enum_.rs b/lib/bindings/langs/react-native/src/gen_typescript/enum_.rs index 418284fc7..3fd0ea92f 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/enum_.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/enum_.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; +use uniffi_bindgen::backend::{CodeType, Literal}; +#[derive(Debug)] pub struct EnumCodeType { id: String, } @@ -15,20 +16,20 @@ impl EnumCodeType { } impl CodeType for EnumCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - oracle.class_name(&self.id) + fn type_label(&self) -> String { + super::TypescriptCodeOracle.class_name(&self.id) } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.id) } - fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String { + fn literal(&self, literal: &Literal) -> String { if let Literal::Enum(v, _) = literal { format!( "{}.{}", - self.type_label(oracle), - oracle.enum_variant_name(v) + self.type_label(), + super::TypescriptCodeOracle.enum_variant_name(v) ) } else { unreachable!(); diff --git a/lib/bindings/langs/react-native/src/gen_typescript/error.rs b/lib/bindings/langs/react-native/src/gen_typescript/error.rs deleted file mode 100644 index 86168a732..000000000 --- a/lib/bindings/langs/react-native/src/gen_typescript/error.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; - -pub struct ErrorCodeType { - id: String, -} - -impl ErrorCodeType { - pub fn new(id: String) -> Self { - Self { id } - } -} - -impl CodeType for ErrorCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - oracle.error_name(&self.id) - } - - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { - format!("Type{}", self.id) - } - - fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String { - unreachable!(); - } -} diff --git a/lib/bindings/langs/react-native/src/gen_typescript/executor.rs b/lib/bindings/langs/react-native/src/gen_typescript/executor.rs new file mode 100644 index 000000000..b488b004c --- /dev/null +++ b/lib/bindings/langs/react-native/src/gen_typescript/executor.rs @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use super::CodeType; + +#[derive(Debug)] +pub struct ForeignExecutorCodeType; + +impl CodeType for ForeignExecutorCodeType { + fn type_label(&self) -> String { + // On Swift, we define a struct to represent a ForeignExecutor + "UniFfiForeignExecutor".into() + } + + fn canonical_name(&self) -> String { + "ForeignExecutor".into() + } + + fn initialization_fn(&self) -> Option { + Some("uniffiInitForeignExecutor".into()) + } +} diff --git a/lib/bindings/langs/react-native/src/gen_typescript/external.rs b/lib/bindings/langs/react-native/src/gen_typescript/external.rs index 9b7d15604..3b55b5949 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/external.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/external.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType}; +use uniffi_bindgen::backend::CodeType; +#[derive(Debug)] pub struct ExternalCodeType { name: String, } @@ -15,15 +16,11 @@ impl ExternalCodeType { } impl CodeType for ExternalCodeType { - fn type_label(&self, _oracle: &dyn CodeOracle) -> String { + fn type_label(&self) -> String { self.name.clone() } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.name) } - - fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String { - nm.into() - } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/miscellany.rs b/lib/bindings/langs/react-native/src/gen_typescript/miscellany.rs index ea1d07c97..77637047f 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/miscellany.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/miscellany.rs @@ -1,27 +1,24 @@ use paste::paste; -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; +use uniffi_bindgen::backend::{CodeType, Literal}; macro_rules! impl_code_type_for_miscellany { ($T:ty, $canonical_name:literal) => { paste! { + #[derive(Debug)] pub struct $T; impl CodeType for $T { - fn type_label(&self, _oracle: &dyn CodeOracle) -> String { + fn type_label(&self) -> String { format!("{}", $canonical_name) } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("{}", $canonical_name) } - fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String { + fn literal(&self, _literal: &Literal) -> String { unreachable!() } - - fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String { - nm.to_string() - } } } }; diff --git a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs index a8bb6912c..e0ca12d27 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs @@ -3,16 +3,16 @@ use std::collections::HashSet; use askama::Template; use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use once_cell::sync::Lazy; -use uniffi_bindgen::backend::{CodeOracle, CodeType, TypeIdentifier}; +use uniffi_bindgen::backend::CodeType; use uniffi_bindgen::interface::*; -use crate::generator::RNConfig; +use crate::generator::Config; mod callback_interface; mod compounds; mod custom; mod enum_; -mod error; +mod executor; mod external; mod miscellany; mod object; @@ -34,12 +34,12 @@ static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { #[template(syntax = "rn", escape = "none", path = "module.ts")] #[allow(dead_code)] pub struct ModuleGenerator<'a> { - config: RNConfig, + config: Config, ci: &'a ComponentInterface, } impl<'a> ModuleGenerator<'a> { - pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { Self { config, ci } } } @@ -63,7 +63,7 @@ impl TypescriptCodeOracle { // // - When adding additional types here, make sure to also add a match arm to the `Types.ts` template. // - To keep things managable, let's try to limit ourselves to these 2 mega-matches - fn create_code_type(&self, type_: TypeIdentifier) -> Box { + fn create_code_type(&self, type_: Type) -> Box { match type_ { Type::UInt8 => Box::new(primitives::UInt8CodeType), Type::Int8 => Box::new(primitives::Int8CodeType), @@ -77,35 +77,39 @@ impl TypescriptCodeOracle { Type::Float64 => Box::new(primitives::Float64CodeType), Type::Boolean => Box::new(primitives::BooleanCodeType), Type::String => Box::new(primitives::StringCodeType), + Type::Bytes => Box::new(primitives::BytesCodeType), Type::Timestamp => Box::new(miscellany::TimestampCodeType), Type::Duration => { unimplemented!("Duration is not implemented") } - Type::Enum(id) => Box::new(enum_::EnumCodeType::new(id)), - Type::Object(id) => Box::new(object::ObjectCodeType::new(id)), - Type::Record(id) => Box::new(record::RecordCodeType::new(id)), - Type::Error(id) => Box::new(error::ErrorCodeType::new(id)), - Type::CallbackInterface(id) => { - Box::new(callback_interface::CallbackInterfaceCodeType::new(id)) + Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)), + Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)), + Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)), + Type::CallbackInterface { name, .. } => { + Box::new(callback_interface::CallbackInterfaceCodeType::new(name)) } - Type::Optional(inner) => Box::new(compounds::OptionalCodeType::new(*inner)), - Type::Sequence(inner) => Box::new(compounds::SequenceCodeType::new(*inner)), - Type::Map(key, value) => Box::new(compounds::MapCodeType::new(*key, *value)), + Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType), + Type::Optional { inner_type } => { + Box::new(compounds::OptionalCodeType::new(*inner_type)) + } + Type::Sequence { inner_type } => { + Box::new(compounds::SequenceCodeType::new(*inner_type)) + } + Type::Map { + key_type, + value_type, + } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)), Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)), Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)), - - Type::Unresolved { name } => { - unreachable!("Type `{name}` must be resolved before calling create_code_type") - } } } } -impl CodeOracle for TypescriptCodeOracle { - fn find(&self, type_: &TypeIdentifier) -> Box { - self.create_code_type(type_.clone()) +impl TypescriptCodeOracle { + fn find(&self, type_: &impl AsType) -> Box { + self.create_code_type(type_.as_type()) } /// Get the idiomatic Typescript rendering of a class name (for enums, records, errors, etc). @@ -127,34 +131,10 @@ impl CodeOracle for TypescriptCodeOracle { fn enum_variant_name(&self, nm: &str) -> String { fixup_keyword(nm.to_string().to_shouty_snake_case(), "Enum".to_string()) } - - /// Get the idiomatic Typescript rendering of an exception name - fn error_name(&self, nm: &str) -> String { - self.class_name(nm) - } - - fn ffi_type_label(&self, ffi_type: &FfiType) -> String { - match ffi_type { - FfiType::Int8 - | FfiType::UInt8 - | FfiType::Int16 - | FfiType::UInt16 - | FfiType::Int32 - | FfiType::UInt32 - | FfiType::Int64 - | FfiType::UInt64 - | FfiType::Float32 - | FfiType::Float64 => "number".to_string(), - FfiType::RustArcPtr(name) => format!("{}SafeHandle", name), - FfiType::RustBuffer(_) => "RustBuffer".to_string(), - FfiType::ForeignBytes => "ForeignBytes".to_string(), - FfiType::ForeignCallback => "ForeignCallback".to_string(), - } - } } pub mod filters { - use uniffi_bindgen::backend::CodeType; + use regex::{Captures, Regex}; use super::*; @@ -162,8 +142,8 @@ pub mod filters { &TypescriptCodeOracle } - pub fn type_name(codetype: &impl CodeType) -> Result { - Ok(codetype.type_label(oracle())) + pub fn type_name(type_: &impl AsType) -> Result { + Ok(oracle().find(type_).type_label()) } /// Get the idiomatic Typescript rendering of a function name. @@ -181,33 +161,33 @@ pub mod filters { Ok(oracle().enum_variant_name(nm)) } - pub fn absolute_type_name(t: &TypeIdentifier) -> Result { - let res: Result = match t { - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + pub fn absolute_type_name(type_: &impl AsType) -> Result { + let res: Result = match type_.as_type() { + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); type_name(unboxed) } - _ => type_name(t), + _ => type_name(type_), }; res } - pub fn return_type_name(t: &TypeIdentifier) -> Result { - let res: Result = match t { - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + pub fn return_type_name(type_: &impl AsType) -> Result { + let res: Result = match type_.as_type() { + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); let name = type_name(unboxed)?; Ok(format!("{name} | null")) } - _ => type_name(t), + _ => type_name(type_), }; res } - pub fn default_value(t: &TypeIdentifier) -> Result { - let res: Result = match t { - Type::Optional(inner) => { - let unboxed = inner.as_ref(); + pub fn default_value(type_: &impl AsType) -> Result { + let res: Result = match type_.as_type() { + Type::Optional { inner_type } => { + let unboxed = inner_type.as_ref(); match unboxed { Type::UInt8 | Type::Int8 @@ -220,8 +200,8 @@ pub mod filters { | Type::Float32 | Type::Float64 => Ok(" = 0".into()), Type::String => Ok(" = \"\"".into()), - Type::Record(_) => Ok(" = {}".into()), - Type::Sequence(_) => Ok(" = []".into()), + Type::Record { .. } => Ok(" = {}".into()), + Type::Sequence { .. } => Ok(" = []".into()), _ => Ok("".into()), } } @@ -233,4 +213,82 @@ pub mod filters { pub fn ignored_function(nm: &str) -> Result { Ok(IGNORED_FUNCTIONS.contains(nm)) } + + pub fn docstring( + docstring: &str, + tabs: &i32, + ci: &ComponentInterface, + ) -> Result { + let docstring = replace_types(docstring, ci)?; + let docstring = replace_variables(&docstring)?; + let docstring = textwrap::indent(&textwrap::dedent(&docstring), " * "); + + let tabs = usize::try_from(*tabs).unwrap_or_default(); + let wrapped_docstring = format!("/**\n{docstring}\n */"); + Ok(textwrap::indent(&wrapped_docstring, &"\t".repeat(tabs))) + } + + fn replace_types(docstring: &str, ci: &ComponentInterface) -> Result { + let re = Regex::new(r"\[([a-zA-Z0-9_:]+)\]").unwrap(); + let replacement = |caps: &Captures| -> Result { + let split: Vec<&str> = caps[1].split("::").collect(); + if let Some(enum_def) = ci.get_enum_definition(split[0]) { + let name = type_name(enum_def)?; + if split.len() == 2 { + let v_name = enum_variant(split[1])?; + return Ok(format!("{{@link {}.{}}}", name, v_name)); + } + return Ok(format!("{{@link {}}}", name)); + } else if let Some(fn_def) = ci.get_function_definition(split[0]) { + let name = fn_name(fn_def.name())?; + return Ok(format!("{{@link {}}}", name)); + } else if let Some(obj_def) = ci.get_object_definition(split[0]) { + let mut name = type_name(obj_def)?; + if split.len() == 2 { + name = fn_name(split[1])?; + } + return Ok(format!("{{@link {}}}", name)); + } else if let Some(rec_def) = ci.get_record_definition(split[0]) { + let name = type_name(rec_def)?; + if split.len() == 2 { + let v_name = var_name(split[1])?; + return Ok(format!("{{@link {}.{}}}", name, v_name)); + } + return Ok(format!("{{@link {}}}", name)); + } else if let Some(cb_def) = ci.get_callback_interface_definition(split[0]) { + let name = type_name(cb_def)?; + return Ok(format!("{{@link {}}}", name)); + } + Ok(caps[0].to_string()) + }; + replace_all(&re, docstring, replacement) + } + + fn replace_variables(docstring: &str) -> Result { + let re = Regex::new(r"`(\w+)`").unwrap(); + let replacement = |caps: &Captures| -> Result { + if let Ok(name) = var_name(&caps[1]) { + return Ok(format!("`{}`", name)); + } + Ok(caps[0].to_string()) + }; + replace_all(&re, docstring, replacement) + } + + fn replace_all( + re: &Regex, + haystack: &str, + replacement: impl Fn(&Captures) -> Result, + ) -> Result { + let mut new = String::new(); + let mut last_match = 0; + for caps in re.captures_iter(haystack) { + let m = caps.get(0).unwrap(); + new.push_str(&haystack[last_match..m.start()]); + new.push_str(&replacement(&caps)?); + last_match = m.end(); + } + new.push_str(&haystack[last_match..]); + Ok(new) + } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/object.rs b/lib/bindings/langs/react-native/src/gen_typescript/object.rs index 9d54bab05..29b80e12b 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/object.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/object.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; +use uniffi_bindgen::backend::{CodeType, Literal}; +#[derive(Debug)] pub struct ObjectCodeType { id: String, } @@ -15,15 +16,15 @@ impl ObjectCodeType { } impl CodeType for ObjectCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - oracle.class_name(&self.id) + fn type_label(&self) -> String { + super::TypescriptCodeOracle.class_name(&self.id) } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.id) } - fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String { + fn literal(&self, _literal: &Literal) -> String { unreachable!(); } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/primitives.rs b/lib/bindings/langs/react-native/src/gen_typescript/primitives.rs index 399506ba7..c1e4a88ac 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/primitives.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/primitives.rs @@ -1,8 +1,8 @@ use paste::paste; -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; -use uniffi_bindgen::interface::{types::Type, Radix}; +use uniffi_bindgen::backend::{CodeType, Literal}; +use uniffi_bindgen::interface::{Radix, Type}; -fn render_literal(_oracle: &dyn CodeOracle, literal: &Literal) -> String { +fn render_literal(literal: &Literal) -> String { fn typed_number(type_: &Type, num_str: String) -> String { match type_ { // Bytes, Shorts and Ints can all be inferred from the type. @@ -46,15 +46,16 @@ fn render_literal(_oracle: &dyn CodeOracle, literal: &Literal) -> String { macro_rules! impl_code_type_for_primitive { ($T:ty, $class_name:literal) => { paste! { + #[derive(Debug)] pub struct $T; impl CodeType for $T { - fn type_label(&self, _oracle: &dyn CodeOracle) -> String { + fn type_label(&self) -> String { $class_name.into() } - fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String { - render_literal(oracle, &literal) + fn literal(&self, literal: &Literal) -> String { + render_literal(literal) } } } @@ -63,6 +64,7 @@ macro_rules! impl_code_type_for_primitive { impl_code_type_for_primitive!(BooleanCodeType, "boolean"); impl_code_type_for_primitive!(StringCodeType, "string"); +impl_code_type_for_primitive!(BytesCodeType, "[]"); impl_code_type_for_primitive!(Int8CodeType, "number"); impl_code_type_for_primitive!(Int16CodeType, "number"); impl_code_type_for_primitive!(Int32CodeType, "number"); diff --git a/lib/bindings/langs/react-native/src/gen_typescript/record.rs b/lib/bindings/langs/react-native/src/gen_typescript/record.rs index 820a4b068..ef446515f 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/record.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/record.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal}; +use uniffi_bindgen::backend::{CodeType, Literal}; +#[derive(Debug)] pub struct RecordCodeType { id: String, } @@ -15,15 +16,15 @@ impl RecordCodeType { } impl CodeType for RecordCodeType { - fn type_label(&self, oracle: &dyn CodeOracle) -> String { - oracle.class_name(&self.id) + fn type_label(&self) -> String { + super::TypescriptCodeOracle.class_name(&self.id) } - fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String { + fn canonical_name(&self) -> String { format!("Type{}", self.id) } - fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String { + fn literal(&self, _literal: &Literal) -> String { unreachable!(); } } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/EnumTemplate.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/EnumTemplate.ts index 05b67828c..08544cf45 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/EnumTemplate.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/EnumTemplate.ts @@ -1,26 +1,28 @@ +{%- if !ci.is_name_used_as_error(name) -%} {%- let e = ci.get_enum_definition(name).unwrap() %} {%- if e.is_flat() %} - +{% call ts::docstring(e, 0, ci) %} export enum {{ type_name }} { - {% for variant in e.variants() -%} - {{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %}, - {% endif %} + {%- for variant in e.variants() -%} + {%- call ts::docstring(variant, 1, ci) %} + {{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %},{% endif %} {%- endfor %} } {%- else %} export enum {{ type_name }}Variant { - {% for variant in e.variants() -%} - {{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %}, - {% endif %} + {%- for variant in e.variants() -%} + {%- call ts::docstring(variant, 1, ci) %} + {{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %},{% endif %} {%- endfor %} } - +{% call ts::docstring(e, 0, ci) %} export type {{ type_name }} = {% for variant in e.variants() -%}{ type: {{ type_name }}Variant.{{ variant.name()|enum_variant }}{% if variant.has_fields() %}, {%- call ts::field_list_decl(variant) -%}{% endif %} }{% if !loop.last %} | {% endif %} {%- endfor %} +{%- endif %} {%- endif %} \ No newline at end of file diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts index c9262aa4a..25b335c18 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts @@ -1,20 +1,22 @@ +{%- call ts::docstring(ci.get_callback_interface_definition("EventListener").unwrap(), 0, ci) %} export type EventListener = (e: SdkEvent) => void - +{% call ts::docstring(ci.get_callback_interface_definition("Logger").unwrap(), 0, ci) %} export type Logger = (logEntry: LogEntry) => void - +{% call ts::docstring(ci.get_function_definition("connect").unwrap(), 0, ci) %} export const connect = async (req: ConnectRequest): Promise => { const response = await BreezSDKLiquid.connect(req) return response } - +{%- let obj = ci.get_object_definition("BindingLiquidSdk").unwrap() %} +{% call ts::docstring(obj.get_method("add_event_listener"), 0, ci) %} export const addEventListener = async (listener: EventListener): Promise => { const response = await BreezSDKLiquid.addEventListener() BreezSDKLiquidEmitter.addListener(`event-${response}`, listener) return response } - +{% call ts::docstring(ci.get_function_definition("set_logger").unwrap(), 0, ci) %} export const setLogger = async (logger: Logger): Promise => { const subscription = BreezSDKLiquidEmitter.addListener("breezSdkLiquidLog", logger) diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/Objects.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/Objects.ts index 7cbe95842..c3b39f934 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/Objects.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/Objects.ts @@ -1,8 +1,9 @@ {%- for type_ in ci.iter_types() %} {%- let type_name = type_|type_name %} {%- match type_ %} -{%- when Type::Object ( name ) %} +{%- when Type::Object { name, module_path, imp } %} {% let obj = ci.get_object_definition(name).unwrap() %} +{%- call ts::docstring(obj, 0, ci) %} {%- for func in obj.methods() -%} {%- if func.name()|ignored_function == false -%} {%- include "TopLevelFunctionTemplate.ts" %} diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/RecordTemplate.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/RecordTemplate.ts index 24e297a29..a67abea1a 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/RecordTemplate.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/RecordTemplate.ts @@ -1,5 +1,6 @@ {%- let rec = ci.get_record_definition(name).unwrap() %} +{%- call ts::docstring(rec, 0, ci) %} export type {{ type_name }} = { {%- call ts::field_list_decl(rec) %} } diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/TopLevelFunctionTemplate.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/TopLevelFunctionTemplate.ts index a8191e87b..432eb0f78 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/TopLevelFunctionTemplate.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/TopLevelFunctionTemplate.ts @@ -1,3 +1,4 @@ +{%- call ts::docstring(func, 0, ci) %} {%- match func.return_type() -%} {%- when Some with (return_type) %} export const {{ func.name()|fn_name }} = async ({%- call ts::arg_list_decl(func) -%}): Promise<{{ return_type|return_type_name }}> => { diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/Types.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/Types.ts index c8fd018cb..390617931 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/Types.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/Types.ts @@ -1,9 +1,9 @@ {%- for type_ in ci.iter_types() %} {%- let type_name = type_|type_name %} {%- match type_ %} -{%- when Type::Record ( name ) %} -{%- include "RecordTemplate.ts" %} -{%- when Type::Enum ( name ) %} +{%- when Type::Record { name, module_path } %} +{% include "RecordTemplate.ts" %} +{%- when Type::Enum { name, module_path } %} {%- include "EnumTemplate.ts" %} {%- else %} {%- endmatch -%} diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/macros.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/macros.ts index 4f8708ee9..8fb221067 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/macros.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/macros.ts @@ -14,19 +14,28 @@ {%- macro field_list_decl(rec) %} {%- for f in rec.fields() %} - {%- match f.type_() %} - {%- when Type::Optional(inner) %} - {%- let unboxed = inner.as_ref() %} + {%- call docstring(f, 1, ci) %} + {%- match f.as_type() %} + {%- when Type::Optional { inner_type } %} + {%- let unboxed = inner_type.as_ref() %} {{ f.name()|var_name }}?: {{ unboxed|type_name }} {%- else %} - {{ f.name()|var_name }}: {{ f.type_()|type_name }} + {{ f.name()|var_name }}: {{ f|type_name }} {%- endmatch %} {%- endfor %} {%- endmacro -%} {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name()|var_name }}: {{ arg.type_()|absolute_type_name }}{{- arg.type_()|default_value -}} + {{ arg.name()|var_name }}: {{ arg|absolute_type_name }}{{- arg|default_value -}} {%- if !loop.last %}, {% endif -%} {%- endfor %} {%- endmacro %} + +{%- macro docstring(defn, indent_tabs, ci) %} +{%- match defn.docstring() %} +{%- when Some(docstring) %} +{{ docstring|docstring(indent_tabs, ci) }} +{%- else -%} +{%- endmatch %} +{%- endmacro %} \ No newline at end of file diff --git a/lib/bindings/langs/react-native/src/generator.rs b/lib/bindings/langs/react-native/src/generator.rs index 266b8d01c..1da7aa7ec 100644 --- a/lib/bindings/langs/react-native/src/generator.rs +++ b/lib/bindings/langs/react-native/src/generator.rs @@ -7,15 +7,17 @@ use std::fs; use std::fs::File; use std::io::Write; use std::process::Command; -use uniffi_bindgen::{BindingGenerator, BindingGeneratorConfig, ComponentInterface}; +use uniffi_bindgen::BindingGenerator; +use uniffi_bindgen::BindingsConfig; +use uniffi_bindgen::ComponentInterface; use crate::gen_kotlin; use crate::gen_swift; use crate::gen_typescript; -pub struct RNBindingGenerator {} +pub struct ReactNativeBindingGenerator {} -impl RNBindingGenerator { +impl ReactNativeBindingGenerator { fn write_bindings( &self, bindings_output: &String, @@ -32,7 +34,7 @@ impl RNBindingGenerator { fn write_kotlin_mapper_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -57,7 +59,7 @@ impl RNBindingGenerator { fn write_kotlin_module_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -92,7 +94,7 @@ impl RNBindingGenerator { fn write_swift_mapper_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -116,7 +118,7 @@ impl RNBindingGenerator { fn write_swift_extern_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -140,7 +142,7 @@ impl RNBindingGenerator { fn write_swift_module_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -177,7 +179,7 @@ impl RNBindingGenerator { fn write_typescript_bindings( &self, ci: &ComponentInterface, - config: RNConfig, + config: Config, base_output_path: &Utf8Path, ) -> Result<()> { // Create the path @@ -210,57 +212,64 @@ impl RNBindingGenerator { } #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct RNConfig { +pub struct Config { + cdylib_name: Option, package_name: Option, } -impl RNConfig {} +impl Config {} -impl BindingGeneratorConfig for RNConfig { - fn get_entry_from_bindings_table(_bindings: &toml::value::Value) -> Option { - if let Some(table) = _bindings.as_table() { - table.get("rn").cloned() - } else { - None - } +impl BindingsConfig for Config { + fn update_from_ci(&mut self, ci: &ComponentInterface) { + self.package_name + .get_or_insert_with(|| ci.namespace().into()); + self.cdylib_name + .get_or_insert_with(|| format!("uniffi_{}", ci.namespace())); + } + + fn update_from_cdylib_name(&mut self, cdylib_name: &str) { + self.cdylib_name + .get_or_insert_with(|| cdylib_name.to_string()); } - fn get_config_defaults(ci: &ComponentInterface) -> Vec<(String, toml::value::Value)> { - vec![ - ( - "package_name".to_string(), - toml::value::Value::String(ci.namespace().to_string()), - ), - ( - "cdylib_name".to_string(), - toml::value::Value::String(ci.namespace().to_string()), - ), - ] + fn update_from_dependency_configs( + &mut self, + _config_map: std::collections::HashMap<&str, &Self>, + ) { + // unused } } -impl BindingGenerator for RNBindingGenerator { - type Config = RNConfig; +impl BindingGenerator for ReactNativeBindingGenerator { + type Config = Config; fn write_bindings( &self, - ci: ComponentInterface, - config: Self::Config, + ci: &ComponentInterface, + config: &Self::Config, out_dir: &Utf8Path, ) -> Result<()> { fs::create_dir_all(out_dir)?; // generate kotlin - self.write_kotlin_mapper_bindings(&ci, config.clone(), out_dir)?; - self.write_kotlin_module_bindings(&ci, config.clone(), out_dir)?; + self.write_kotlin_mapper_bindings(ci, config.clone(), out_dir)?; + self.write_kotlin_module_bindings(ci, config.clone(), out_dir)?; // generate ios - self.write_swift_mapper_bindings(&ci, config.clone(), out_dir)?; - self.write_swift_extern_bindings(&ci, config.clone(), out_dir)?; - self.write_swift_module_bindings(&ci, config.clone(), out_dir)?; + self.write_swift_mapper_bindings(ci, config.clone(), out_dir)?; + self.write_swift_extern_bindings(ci, config.clone(), out_dir)?; + self.write_swift_module_bindings(ci, config.clone(), out_dir)?; // generate typescript - self.write_typescript_bindings(&ci, config.clone(), out_dir)?; + self.write_typescript_bindings(ci, config.clone(), out_dir)?; + Ok(()) + } + + fn check_library_path( + &self, + _library_path: &Utf8Path, + _cdylib_name: Option<&str>, + ) -> anyhow::Result<()> { Ok(()) } } diff --git a/lib/bindings/langs/react-native/src/main.rs b/lib/bindings/langs/react-native/src/main.rs index 8d3f53aa1..d61be5471 100644 --- a/lib/bindings/langs/react-native/src/main.rs +++ b/lib/bindings/langs/react-native/src/main.rs @@ -2,16 +2,27 @@ mod gen_kotlin; mod gen_swift; mod gen_typescript; mod generator; -use camino::Utf8Path; +use camino::{Utf8Path, Utf8PathBuf}; use clap::Parser; -use generator::RNBindingGenerator; +use generator::ReactNativeBindingGenerator; #[derive(Parser, Debug)] pub(crate) struct Cli { + /// Path to the UDL file #[clap(name = "binding_dir", short = 'b', long = "binding_dir")] pub(crate) binding_dir: Option, + + /// Directory in which to write generated files. Default is same folder as .udl file. #[clap(name = "out_dir", short = 'o', long = "out_dir")] pub(crate) out_dir: Option, + + /// Extract proc-macro metadata from a native lib (cdylib or staticlib) for this crate. + #[clap(long, short)] + lib_file: Option, + + /// This as the crate name instead of attempting to locate and parse Cargo.toml. + #[clap(long = "crate")] + crate_name: Option, } fn main() { @@ -25,10 +36,12 @@ fn main() { // React Native generator uniffi_bindgen::generate_external_bindings( - RNBindingGenerator {}, + ReactNativeBindingGenerator {}, udl_file, Some(config), Some(out_dir), + cli.lib_file, + cli.crate_name.as_deref(), ) .unwrap(); } diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index f2e05f47d..15f15c57f 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -1,7 +1,4 @@ -////////////////////////////////// -// BEGIN sdk-common mirror imports -// These are structs defined in sdk-common, which we want to make available in this project's UDL bindings - +/// Wrapper for a BOLT11 LN invoice dictionary LNInvoice { string bolt11; Network network; @@ -17,40 +14,82 @@ dictionary LNInvoice { u64 min_final_cltv_expiry_delta; }; +/// The different supported bitcoin networks enum Network { + /// Mainnet "Bitcoin", "Testnet", "Signet", "Regtest", }; +/// A route hint for a LN payment dictionary RouteHint { sequence hops; }; +/// Details of a specific hop in a larger route hint dictionary RouteHintHop { - string src_node_id; - string short_channel_id; - u32 fees_base_msat; - u32 fees_proportional_millionths; - u64 cltv_expiry_delta; - u64? htlc_minimum_msat; - u64? htlc_maximum_msat; -}; - + /// The node id of the non-target end of the route + string src_node_id; + /// The short channel id of this channel + string short_channel_id; + /// The fees which must be paid to use this channel + u32 fees_base_msat; + u32 fees_proportional_millionths; + /// The difference in CLTV values between this node and the next node + u64 cltv_expiry_delta; + /// The minimum value, in msat, which must be relayed to the next hop + u64? htlc_minimum_msat; + /// The maximum value in msat available for routing with a single HTLC + u64? htlc_maximum_msat; +}; + +/// Different kinds of inputs supported by [parse], including any relevant details extracted from the input [Enum] interface InputType { + /// # Supported standards + /// + /// - plain on-chain BTC address + /// - BIP21 BitcoinAddress(BitcoinAddressData address); + /// # Supported standards + /// + /// - plain on-chain liquid address + /// - BIP21 on liquid/liquidtestnet LiquidAddress(LiquidAddressData address); Bolt11(LNInvoice invoice); NodeId(string node_id); Url(string url); + /// # Supported standards + /// + /// - LUD-01 LNURL bech32 encoding + /// - LUD-06 `payRequest` spec + /// - LUD-16 LN Address + /// - LUD-17 Support for lnurlp prefix with non-bech32-encoded LNURL URLs LnUrlPay(LnUrlPayRequestData data); + /// # Supported standards + /// + /// - LUD-01 LNURL bech32 encoding + /// - LUD-03 `withdrawRequest` spec + /// - LUD-17 Support for lnurlw prefix with non-bech32-encoded LNURL URLs + /// + /// # Not supported (yet) + /// + /// - LUD-14 `balanceCheck`: reusable `withdrawRequest`s + /// - LUD-19 Pay link discoverable from withdraw link LnUrlWithdraw(LnUrlWithdrawRequestData data); + /// # Supported standards + /// + /// - LUD-01 LNURL bech32 encoding + /// - LUD-04 `auth` base spec + /// - LUD-17 Support for keyauth prefix with non-bech32-encoded LNURL URLs LnUrlAuth(LnUrlAuthRequestData data); + /// Error returned by the LNURL endpoint LnUrlError(LnUrlErrorData data); }; +/// Wrapped in a [InputType::BitcoinAddress], this is the result of [parse] when given a plain or BIP-21 BTC address. dictionary BitcoinAddressData { string address; Network network; @@ -59,6 +98,7 @@ dictionary BitcoinAddressData { string? message; }; +/// Wrapped in a [InputType::LiquidAddress], this is the result of [parse] when given a plain or BIP-21 Liquid address. dictionary LiquidAddressData { string address; Network network; @@ -68,33 +108,76 @@ dictionary LiquidAddressData { string? message; }; +/// Wrapped in a [InputType::LnUrlPay], this is the result of [parse] when given a LNURL-pay endpoint. +/// +/// It represents the endpoint's parameters for the LNURL workflow. +/// +/// See dictionary LnUrlPayRequestData { string callback; + /// The minimum amount, in millisats, that this LNURL-pay endpoint accepts u64 min_sendable; + /// The maximum amount, in millisats, that this LNURL-pay endpoint accepts u64 max_sendable; + /// As per LUD-06, `metadata` is a raw string (e.g. a json representation of the inner map) string metadata_str; + /// The comment length accepted by this endpoint + /// + /// See u16 comment_allowed; + /// Indicates the domain of the LNURL-pay service, to be shown to the user when asking for + /// payment input, as per LUD-06 spec. + /// + /// Note: this is not the domain of the callback, but the domain of the LNURL-pay endpoint. string domain; + /// Value indicating whether the recipient supports Nostr Zaps through NIP-57. + /// + /// See boolean allows_nostr; + /// Optional recipient's lnurl provider's Nostr pubkey for NIP-57. If it exists it should be a + /// valid BIP 340 public key in hex. + /// + /// See + /// See string? nostr_pubkey; + /// If sending to a LN Address, this will be filled. string? ln_address; }; +/// Wrapped in a [InputType::LnUrlWithdraw], this is the result of [parse] when given a LNURL-withdraw endpoint. +/// +/// It represents the endpoint's parameters for the LNURL workflow. +/// +/// See dictionary LnUrlWithdrawRequestData { string callback; string k1; string default_description; + /// The minimum amount, in millisats, that this LNURL-withdraw endpoint accepts u64 min_withdrawable; + /// The maximum amount, in millisats, that this LNURL-withdraw endpoint accepts u64 max_withdrawable; }; +/// Wrapped in a [InputType::LnUrlAuth], this is the result of [parse] when given a LNURL-auth endpoint. +/// +/// It represents the endpoint's parameters for the LNURL workflow. +/// +/// See dictionary LnUrlAuthRequestData { + /// Hex encoded 32 bytes of challenge string k1; + /// Indicates the domain of the LNURL-auth service, to be shown to the user when asking for + /// auth confirmation, as per LUD-04 spec. string domain; + /// Indicates the URL of the LNURL-auth service, including the query arguments. This will be + /// extended with the signed challenge and the linking key, then called in the second step of the workflow. string url; + /// When available, one of: register, login, link, auth string? action = null; }; +/// Wrapped in a [InputType::LnUrlError], this represents a LNURL-endpoint error. dictionary LnUrlErrorData { string reason; }; @@ -106,24 +189,35 @@ interface SuccessActionProcessed { Url(UrlSuccessActionData data); }; +/// Result of decryption of [SuccessActionProcessed::Aes] payload [Enum] interface AesSuccessActionDataResult { Decrypted(AesSuccessActionDataDecrypted data); ErrorStatus(string reason); }; +/// Wrapper for the decrypted [AesSuccessActionDataResult::Decrypted] payload dictionary AesSuccessActionDataDecrypted { + /// Contents description, up to 144 characters string description; + /// Decrypted content string plaintext; }; +/// Wrapper for the [SuccessActionProcessed::Message] payload dictionary MessageSuccessActionData { string message; }; +/// Wrapper for the [SuccessActionProcessed::Url] payload dictionary UrlSuccessActionData { + /// Contents description, up to 144 characters string description; + /// URL of the success action string url; + /// Indicates the success URL domain matches the LNURL callback domain. + /// + /// See boolean matches_callback_domain; }; @@ -132,40 +226,74 @@ dictionary LnUrlPayErrorData { string reason; }; +/// Represents a LNURL-pay request. dictionary LnUrlPayRequest { + /// The [LnUrlPayRequestData] returned by [parse] LnUrlPayRequestData data; + /// The amount in millisatoshis for this payment u64 amount_msat; + /// An optional comment for this payment string? comment = null; + /// The external label or identifier of the [Payment] string? payment_label = null; + /// Validates that, if there is a URL success action, the URL domain matches + /// the LNURL callback domain. Defaults to `true` boolean? validate_success_action_url = null; }; +/// Error returned by [BindingLiquidSdk::lnurl_pay] [Error] interface LnUrlPayError { + /// This error is raised when attempting to pay an invoice that has already being paid. AlreadyPaid(); + /// This error is raised when a general error occurs not specific to other error variants + /// in this enum. Generic(string err); + /// This error is raised when the amount from the parsed invoice is not set. InvalidAmount(string err); + /// This error is raised when the lightning invoice cannot be parsed. InvalidInvoice(string err); + /// This error is raised when the lightning invoice is for a different Bitcoin network. InvalidNetwork(string err); + /// This error is raised when the decoded LNURL URI is not compliant to the specification. InvalidUri(string err); + /// This error is raised when the lightning invoice has passed it's expiry time. InvoiceExpired(string err); + /// This error is raised when attempting to make a payment by the node fails. PaymentFailed(string err); + /// This error is raised when attempting to make a payment takes too long. PaymentTimeout(string err); + /// This error is raised when no route can be found when attempting to make a + /// payment by the node. RouteNotFound(string err); + /// This error is raised when the route is considered too expensive when + /// attempting to make a payment by the node. RouteTooExpensive(string err); + /// This error is raised when a connection to an external service fails. ServiceConnectivity(string err); }; +/// Error returned by [BindingLiquidSdk::lnurl_withdraw] [Error] interface LnUrlWithdrawError { + /// This error is raised when a general error occurs not specific to other error variants + /// in this enum. Generic(string err); + /// This error is raised when the amount is zero or the amount does not cover + /// the cost to open a new channel. InvalidAmount(string err); + /// This error is raised when the lightning invoice cannot be parsed. InvalidInvoice(string err); + /// This error is raised when the decoded LNURL URI is not compliant to the specification. InvalidUri(string err); - ServiceConnectivity(string err); + /// This error is raised when no routing hints were able to be added to the invoice + /// while trying to receive a payment. InvoiceNoRoutingHints(string err); + /// This error is raised when a connection to an external service fails. + ServiceConnectivity(string err); }; +/// [LnUrlCallbackStatus] specific to LNURL-withdraw, where the success case contains the invoice. [Enum] interface LnUrlWithdrawResult { Ok(LnUrlWithdrawSuccessData data); @@ -177,33 +305,47 @@ dictionary LnUrlWithdrawSuccessData { LNInvoice invoice; }; -dictionary LnUrlWithdrawRequestData { - string callback; - string k1; - string default_description; - u64 min_withdrawable; - u64 max_withdrawable; -}; - dictionary LnUrlWithdrawRequest { + /// Request data containing information on how to call the lnurl withdraw + /// endpoint. Typically retrieved by calling [parse] on a lnurl withdraw + /// input. LnUrlWithdrawRequestData data; + /// The amount to withdraw from the lnurl withdraw endpoint. Must be between + /// `min_withdrawable` and `max_withdrawable`. u64 amount_msat; + /// Optional description that will be put in the payment request for the + /// lnurl withdraw endpoint. string? description = null; }; +/// Contains the result of the entire LNURL interaction, as reported by the LNURL endpoint. +/// +/// * [LnUrlCallbackStatus::Ok] indicates the interaction with the endpoint was valid, and the endpoint +/// - started to pay the invoice asynchronously in the case of LNURL-withdraw, +/// - verified the client signature in the case of LNURL-auth +/// * [LnUrlCallbackStatus::ErrorStatus] indicates a generic issue the LNURL endpoint encountered, including a freetext +/// description of the reason. +/// +/// Both cases are described in LUD-03 & LUD-04: [Enum] interface LnUrlCallbackStatus { Ok(); ErrorStatus(LnUrlErrorData data); }; +/// Error returned by [BindingLiquidSdk::lnurl_auth] [Error] interface LnUrlAuthError { + /// This error is raised when a general error occurs not specific to other error variants + /// in this enum. Generic(string err); + /// This error is raised when the decoded LNURL URI is not compliant to the specification. InvalidUri(string err); + /// This error is raised when a connection to an external service fails. ServiceConnectivity(string err); }; +/// Denominator in an exchange rate dictionary Rate { string coin; f64 value; @@ -214,6 +356,7 @@ dictionary FiatCurrency { CurrencyInfo info; }; +/// Settings for the symbol representation of a currency dictionary Symbol { string? grapheme; string? template; @@ -221,17 +364,20 @@ dictionary Symbol { u32? position; }; +/// Locale-specific settings for the representation of a currency dictionary LocaleOverrides { string locale; u32? spacing; Symbol symbol; }; +/// Localized name of a currency dictionary LocalizedName { string locale; string name; }; +/// Details about a supported currency in the fiat rate feed dictionary CurrencyInfo { string name; u32 fraction_size; @@ -242,13 +388,22 @@ dictionary CurrencyInfo { sequence locale_overrides; }; +// ---------------------------------------------------------------------------------------------------------------- // END sdk-common mirror imports -//////////////////////////////// - -////////////////////////////////// -// BEGIN sdk-common wrappers -// These are connecting structures that glue relevant sdk-common structs to the SDK - +// These are structs defined in sdk-common, which we want to make available in this project's UDL bindings +// ---------------------------------------------------------------------------------------------------------------- + +/// Contains the result of the entire LNURL-pay interaction, as reported by the LNURL endpoint. +/// +/// * [LnUrlPayResult::EndpointSuccess] indicates the payment is complete. The endpoint may return a [SuccessActionProcessed], +/// in which case, the wallet has to present it to the user as described in +/// +/// +/// * [LnUrlPayResult::EndpointError] indicates a generic issue the LNURL endpoint encountered, including a freetext +/// field with the reason. +/// +/// * [LnUrlPayResult::PayError] indicates that an error occurred while trying to pay the invoice from the LNURL endpoint. +/// This includes the payment hash of the failed invoice and the failure reason. [Enum] interface LnUrlPayResult { EndpointSuccess(LnUrlPaySuccessData data); @@ -261,8 +416,10 @@ dictionary LnUrlPaySuccessData { Payment payment; }; +// ---------------------------------------------------------------------------------------------------------------- // END sdk-common wrappers -//////////////////////////////// +// These are connecting structures that glue relevant sdk-common structs to the SDK +// ---------------------------------------------------------------------------------------------------------------- [Error] enum SdkError { @@ -297,192 +454,305 @@ enum PaymentError { "SignerError", }; +/// Configuration for the Liquid SDK dictionary Config { string liquid_electrum_url; string bitcoin_electrum_url; + /// The mempool.space API URL, has to be in the format: https://mempool.space/api string mempoolspace_url; + /// Directory in which all SDK files (DB, log, cache) are stored. + /// + /// Prefix can be a relative or absolute path to this directory. string working_dir; LiquidNetwork network; + /// Send payment timeout. See [BindingLiquidSdk::send_payment] u64 payment_timeout_sec; + /// Zero-conf minimum accepted fee-rate in millisatoshis per vbyte u32 zero_conf_min_fee_rate_msat; + /// Maximum amount in satoshi to accept zero-conf payments with + /// Defaults to [crate::receive_swap::DEFAULT_ZERO_CONF_MAX_SAT] u64? zero_conf_max_amount_sat; + /// The Breez API key used for making requests to their mempool service string? breez_api_key; }; +/// Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the +/// Bitcoin network used. enum LiquidNetwork { + /// Mainnet Bitcoin and Liquid chains "Mainnet", + /// Testnet Bitcoin and Liquid chains "Testnet", }; +/// An argument when calling [BindingLiquidSdk::connect]. dictionary ConnectRequest { Config config; string mnemonic; }; +/// Returned when calling [BindingLiquidSdk::get_info]. dictionary GetInfoResponse { + /// Usable balance. This is the confirmed onchain balance minus `pending_send_sat`. u64 balance_sat; + /// Amount that is being used for ongoing Send swaps u64 pending_send_sat; + /// Incoming amount that is pending from ongoing Receive swaps u64 pending_receive_sat; string pubkey; }; +/// An argument when calling [BindingLiquidSdk::sign_message]. dictionary SignMessageRequest { string message; }; +/// Returned when calling [BindingLiquidSdk::sign_message]. dictionary SignMessageResponse { string signature; }; +/// An argument when calling [BindingLiquidSdk::check_message]. dictionary CheckMessageRequest { + /// The message that was signed. string message; + /// The public key of the node that signed the message. string pubkey; + /// The zbase encoded signature to verify. string signature; }; +/// Returned when calling [BindingLiquidSdk::check_message]. dictionary CheckMessageResponse { + /// Boolean value indicating whether the signature covers the message and + /// was signed by the given pubkey. boolean is_valid; }; +/// An argument when calling [BindingLiquidSdk::prepare_send_payment]. dictionary PrepareSendRequest { + /// The destination we intend to pay to. + /// Supports BIP21 URIs, BOLT11 invoices and Liquid addresses string destination; + /// Should only be set when paying directly onchain or to a BIP21 URI + /// where no amount is specified u64? amount_sat = null; }; +/// Specifies the supported destinations which can be payed by the SDK [Enum] interface SendDestination { LiquidAddress(LiquidAddressData address_data); Bolt11(LNInvoice invoice); }; +/// Returned when calling [BindingLiquidSdk::prepare_send_payment]. dictionary PrepareSendResponse { SendDestination destination; u64 fees_sat; }; +/// An argument when calling [BindingLiquidSdk::send_payment]. dictionary SendPaymentRequest { PrepareSendResponse prepare_response; }; +/// Returned when calling [BindingLiquidSdk::send_payment]. dictionary SendPaymentResponse { Payment payment; }; +/// The send/receive methods supported by the SDK enum PaymentMethod { "Lightning", "BitcoinAddress", "LiquidAddress", }; +/// An argument when calling [BindingLiquidSdk::prepare_receive_payment]. dictionary PrepareReceiveRequest { PaymentMethod payment_method; u64? payer_amount_sat = null; }; +/// Returned when calling [BindingLiquidSdk::prepare_receive_payment]. dictionary PrepareReceiveResponse { u64? payer_amount_sat; PaymentMethod payment_method; u64 fees_sat; }; +/// An argument when calling [BindingLiquidSdk::receive_payment]. dictionary ReceivePaymentRequest { PrepareReceiveResponse prepare_response; + /// The description for this payment request. string? description = null; + /// If set to true, then the hash of the description will be used. boolean? use_description_hash = null; }; +/// Returned when calling [BindingLiquidSdk::receive_payment]. dictionary ReceivePaymentResponse { + /// Either a BIP21 URI (Liquid or Bitcoin), a Liquid address + /// or an invoice, depending on the [PrepareReceiveResponse] parameters string destination; }; +/// The minimum and maximum in satoshis of a Lightning or onchain payment. dictionary Limits { u64 min_sat; u64 max_sat; u64 max_zero_conf_sat; }; +/// Returned when calling [BindingLiquidSdk::fetch_lightning_limits]. dictionary LightningPaymentLimitsResponse { + /// Amount limits for a Send Payment to be valid Limits send; + /// Amount limits for a Receive Payment to be valid Limits receive; }; +/// Returned when calling [BindingLiquidSdk::fetch_onchain_limits]. dictionary OnchainPaymentLimitsResponse { + /// Amount limits for a Send Onchain Payment to be valid Limits send; + /// Amount limits for a Receive Onchain Payment to be valid Limits receive; }; [Enum] interface PayOnchainAmount { + /// The amount in satoshi that will be received Receiver(u64 amount_sat); + /// Indicates that all available funds should be sent Drain(); }; +/// An argument when calling [BindingLiquidSdk::prepare_pay_onchain]. dictionary PreparePayOnchainRequest { PayOnchainAmount amount; + /// The optional fee rate of the Bitcoin claim transaction in sat/vB. Defaults to the swapper estimated claim fee. u32? fee_rate_sat_per_vbyte = null; }; +/// Returned when calling [BindingLiquidSdk::prepare_pay_onchain]. dictionary PreparePayOnchainResponse { u64 receiver_amount_sat; u64 claim_fees_sat; u64 total_fees_sat; }; +/// An argument when calling [BindingLiquidSdk::pay_onchain]. dictionary PayOnchainRequest { string address; PreparePayOnchainResponse prepare_response; }; +/// An argument of [PrepareBuyBitcoinRequest] when calling [BindingLiquidSdk::prepare_buy_bitcoin]. enum BuyBitcoinProvider { "Moonpay", }; +/// An argument when calling [BindingLiquidSdk::prepare_buy_bitcoin]. dictionary PrepareBuyBitcoinRequest { BuyBitcoinProvider provider; u64 amount_sat; }; +/// Returned when calling [BindingLiquidSdk::prepare_buy_bitcoin]. dictionary PrepareBuyBitcoinResponse { BuyBitcoinProvider provider; u64 amount_sat; u64 fees_sat; }; +/// An argument when calling [BindingLiquidSdk::buy_bitcoin]. dictionary BuyBitcoinRequest { PrepareBuyBitcoinResponse prepare_response; + /// The optional URL to redirect to after completing the buy. + /// + /// For Moonpay, see string? redirect_url = null; }; +/// An argument when calling [BindingLiquidSdk::backup]. dictionary BackupRequest { + /// Path to the backup. + /// + /// If not set, it defaults to 'backup.sql' for mainnet and 'backup-testnet.sql' for testnet. + /// The file will be saved in [ConnectRequest]'s `data_dir`. string? backup_path = null; }; +/// An argument when calling [BindingLiquidSdk::restore]. dictionary RestoreRequest { string? backup_path = null; }; +/// An argument when calling [BindingLiquidSdk::list_payments]. dictionary ListPaymentsRequest { sequence? filters = null; + /// Epoch time, in seconds i64? from_timestamp = null; + /// Epoch time, in seconds i64? to_timestamp = null; u32? offset = null; u32? limit = null; }; +/// The specific details of a payment, depending on its type [Enum] interface PaymentDetails { + /// Swapping to or from Lightning Lightning(string swap_id, string description, string? preimage, string? bolt11, string? refund_tx_id, u64? refund_tx_amount_sat); + /// Direct onchain payment to a Liquid address Liquid(string destination, string description); + /// Swapping to or from the Bitcoin chain Bitcoin(string swap_id, string description, string? refund_tx_id, u64? refund_tx_amount_sat); }; +/// Represents an SDK payment. +/// +/// By default, this is an onchain tx. It may represent a swap, if swap metadata is available. dictionary Payment { + /// Composite timestamp that can be used for sorting or displaying the payment. + /// + /// If this payment has an associated swap, it is the swap creation time. Otherwise, the point + /// in time when the underlying tx was included in a block. If there is no associated swap + /// available and the underlying tx is not yet confirmed, the value is 'now()'. u32 timestamp; + /// The payment amount, which corresponds to the onchain tx amount. + /// + /// In case of an outbound payment (Send), this is the payer amount. Otherwise it's the receiver amount. u64 amount_sat; + /// Represents the fees paid by this wallet for this payment. + /// + /// ### Swaps + /// If there is an associated Send Swap, these fees represent the total fees paid by this wallet + /// (the sender). It is the difference between the amount that was sent and the amount received. + /// + /// If there is an associated Receive Swap, these fees represent the total fees paid by this wallet + /// (the receiver). It is also the difference between the amount that was sent and the amount received. + /// + /// ### Pure onchain txs + /// If no swap is associated with this payment: + /// - for Send payments, this is the onchain tx fee + /// - for Receive payments, this is zero u64 fees_sat; + /// If it is a [PaymentType::Send] or [PaymentType::Receive] payment PaymentType payment_type; + /// Composite status representing the overall status of the payment. + /// + /// If the tx has no associated swap, this reflects the onchain tx status (confirmed or not). + /// + /// If the tx has an associated swap, this is determined by the swap status (pending or complete). PaymentState status; + /// The details of a payment, depending on its [Payment::destination] `destination` and + /// [Payment::payment_type] `type`. PaymentDetails details; + /// The destination associated with the payment, if it was created via our SDK. + /// Can be either a Liquid/Bitcoin address, a Liquid BIP21 URI or an invoice string? destination = null; string? tx_id = null; }; @@ -492,22 +762,75 @@ enum PaymentType { "Send", }; +/// The payment state of an individual payment. enum PaymentState { "Created", + /// ## Receive Swaps + /// + /// Covers the cases when + /// - the lockup tx is seen in the mempool or + /// - our claim tx is broadcast + /// + /// When the claim tx is broadcast, `claim_tx_id` is set in the swap. + /// + /// ## Send Swaps + /// + /// This is the status when our lockup tx was broadcast + /// + /// ## Chain Swaps + /// + /// This is the status when the user lockup tx was broadcast + /// + /// ## No swap data available + /// + /// If no associated swap is found, this indicates the underlying tx is not confirmed yet. "Pending", + /// ## Receive Swaps + /// + /// Covers the case when the claim tx is confirmed. + /// + /// ## Send and Chain Swaps + /// + /// This is the status when the claim tx is broadcast and we see it in the mempool. + /// + /// ## No swap data available + /// + /// If no associated swap is found, this indicates the underlying tx is confirmed. "Complete", + /// ## Receive Swaps + /// + /// This is the status when the swap failed for any reason and the Receive could not complete. + /// + /// ## Send and Chain Swaps + /// + /// This is the status when a swap refund was initiated and the refund tx is confirmed. "Failed", + /// ## Send and Outgoing Chain Swaps + /// + /// This covers the case when the swap state is still Created and the swap fails to reach the + /// Pending state in time. The TimedOut state indicates the lockup tx should never be broadcast. "TimedOut", + /// ## Incoming Chain Swaps + /// + /// This covers the case when the swap failed for any reason and there is a user lockup tx. + /// The swap in this case has to be manually refunded with a provided Bitcoin address "Refundable", + /// ## Send and Chain Swaps + /// + /// This is the status when a refund was initiated and/or our refund tx was broadcast + /// + /// When the refund tx is broadcast, `refund_tx_id` is set in the swap. "RefundPending", }; +/// Returned when calling [BindingLiquidSdk::list_refundables]. dictionary RefundableSwap { string swap_address; u32 timestamp; u64 amount_sat; }; +/// Returned when calling [BindingLiquidSdk::recommended_fees]. dictionary RecommendedFees { u64 fastest_fee; u64 half_hour_fee; @@ -516,28 +839,40 @@ dictionary RecommendedFees { u64 minimum_fee; }; +/// An argument when calling [BindingLiquidSdk::prepare_refund]. dictionary PrepareRefundRequest { + /// The address where the swap funds are locked up string swap_address; + /// The address to refund the swap funds to string refund_address; + /// The fee rate in sat/vB for the refund transaction u32 fee_rate_sat_per_vbyte; }; +/// Returned when calling [BindingLiquidSdk::prepare_refund]. dictionary PrepareRefundResponse { u32 tx_vsize; u64 tx_fee_sat; string? refund_tx_id = null; }; +/// An argument when calling [BindingLiquidSdk::refund]. dictionary RefundRequest { + /// The address where the swap funds are locked up string swap_address; + /// The address to refund the swap funds to string refund_address; + /// The fee rate in sat/vB for the refund transaction u32 fee_rate_sat_per_vbyte; }; +/// Returned when calling [BindingLiquidSdk::refund]. dictionary RefundResponse { string refund_tx_id; }; +/// Event emitted by the SDK. Add an [EventListener] by calling [BindingLiquidSdk::add_event_listener] +/// to listen for emitted events. [Enum] interface SdkEvent { PaymentFailed(Payment details); @@ -549,10 +884,12 @@ interface SdkEvent { Synced(); }; +/// Interface that can be used to receive [SdkEvent]s emitted by the SDK. callback interface EventListener { void on_event(SdkEvent e); }; +/// Interface that can be used to receive [LogEntry]s emitted by the SDK. callback interface Logger { void log(LogEntry l); }; @@ -563,115 +900,300 @@ dictionary LogEntry { }; namespace breez_sdk_liquid { + /// Initializes the SDK services and starts the background tasks. + /// This must be called to create the [BindingLiquidSdk] instance. + /// + /// # Arguments + /// + /// * `req` - the [ConnectRequest] containing: + /// * `mnemonic` - the Liquid wallet mnemonic + /// * `config` - the SDK [Config] [Throws=SdkError] BindingLiquidSdk connect(ConnectRequest req); + /// If used, this must be called before [connect]. [Throws=SdkError] void set_logger(Logger logger); + /// Get the full default [Config] for specific [LiquidNetwork]. Config default_config(LiquidNetwork network); + /// Parses a string into an [InputType]. [Throws=PaymentError] InputType parse(string input); + /// Parses a string into an [LNInvoice]. [Throws=PaymentError] LNInvoice parse_invoice(string input); }; interface BindingLiquidSdk { + /// Adds an event listener to the [LiquidSdk] instance, where all [SdkEvent]'s will be emitted to. + /// The event listener can be removed be calling [BindingLiquidSdk::remove_event_listener]. + /// + /// # Arguments + /// + /// * `listener` - The listener which is an implementation of the [EventListener] trait [Throws=SdkError] string add_event_listener(EventListener listener); + /// Removes an event listener from the [BindingLiquidSdk] instance. + /// + /// # Arguments + /// + /// * `id` - the event listener id returned by [BindingLiquidSdk::add_event_listener] [Throws=SdkError] void remove_event_listener(string id); + /// Get the wallet info, calculating the current pending and confirmed balances. [Throws=SdkError] GetInfoResponse get_info(); + /// Sign given message with the private key. Returns a zbase encoded signature. [Throws=SdkError] SignMessageResponse sign_message(SignMessageRequest req); + /// Check whether given message was signed by the given + /// pubkey and the signature (zbase encoded) is valid. [Throws=SdkError] CheckMessageResponse check_message(CheckMessageRequest req); + /// Prepares to pay a Lightning invoice via a submarine swap. + /// + /// # Arguments + /// + /// * `req` - the [PrepareSendRequest] containing: + /// * `destination` - Either a Liquid BIP21 URI/address or a BOLT11 invoice + /// * `amount_sat` - Should only be specified when paying directly onchain or via amount-less BIP21 + /// + /// # Returns + /// Returns a [PrepareSendResponse] containing: + /// * `destination` - the parsed destination, of type [SendDestination] + /// * `fees_sat` - the additional fees which will be paid by the sender [Throws=PaymentError] PrepareSendResponse prepare_send_payment(PrepareSendRequest req); + /// Either pays a Lightning invoice via a submarine swap or sends funds directly to an address. + /// + /// Depending on [Config]'s `payment_timeout_sec`, this function will return: + /// * [PaymentState::Pending] payment - if the payment could be initiated but didn't yet + /// complete in this time + /// * [PaymentState::Complete] payment - if the payment was successfully completed in this time + /// + /// # Arguments + /// + /// * `req` - A [SendPaymentRequest], containing: + /// * `prepare_response` - the [PrepareSendResponse] returned by [BindingLiquidSdk::prepare_send_payment] + /// + /// # Errors + /// + /// * [PaymentError::PaymentTimeout] - if the payment could not be initiated in this time [Throws=PaymentError] SendPaymentResponse send_payment(SendPaymentRequest req); + /// Prepares to receive a Lightning payment via a reverse submarine swap. + /// + /// # Arguments + /// + /// * `req` - the [PrepareReceiveRequest] containing: + /// * `payer_amount_sat` - the amount in satoshis to be paid by the payer + /// * `payment_method` - the supported payment methods; either an invoice, a Liquid address or a Bitcoin address [Throws=PaymentError] PrepareReceiveResponse prepare_receive_payment(PrepareReceiveRequest req); + /// Receive a Lightning payment via a reverse submarine swap, a chain swap or via direct Liquid + /// payment. + /// + /// # Arguments + /// + /// * `req` - the [ReceivePaymentRequest] containing: + /// * `prepare_response` - the [PrepareReceiveResponse] from calling [BindingLiquidSdk::prepare_receive_payment] + /// * `description` - the optional payment description + /// * `use_description_hash` - optional if true uses the hash of the description + /// + /// # Returns + /// + /// * A [ReceivePaymentResponse] containing: + /// * `destination` - the final destination to be paid by the payer, either a BIP21 URI (Liquid or Bitcoin), a Liquid address or an invoice [Throws=PaymentError] ReceivePaymentResponse receive_payment(ReceivePaymentRequest req); + /// Fetch the current payment limits for [BindingLiquidSdk::send_payment] and [BindingLiquidSdk::receive_payment]. [Throws=PaymentError] LightningPaymentLimitsResponse fetch_lightning_limits(); + /// Fetch the current payment limits for [BindingLiquidSdk::pay_onchain] and [BindingLiquidSdk::receive_onchain]. [Throws=PaymentError] OnchainPaymentLimitsResponse fetch_onchain_limits(); + /// Prepares to pay to a Bitcoin address via a chain swap. + /// + /// # Arguments + /// + /// * `req` - the [PreparePayOnchainRequest] containing: + /// * `amount` - which can be of two types: [PayOnchainAmount::Drain], which uses all funds, + /// and [PayOnchainAmount::Receiver], which sets the amount the receiver should receive + /// * `fee_rate_sat_per_vbyte` - the optional fee rate of the Bitcoin claim transaction. Defaults to the swapper estimated claim fee [Throws=PaymentError] PreparePayOnchainResponse prepare_pay_onchain(PreparePayOnchainRequest req); + /// Pays to a Bitcoin address via a chain swap. + /// + /// Depending on [Config]'s `payment_timeout_sec`, this function will return: + /// * [PaymentState::Pending] payment - if the payment could be initiated but didn't yet + /// complete in this time + /// * [PaymentState::Complete] payment - if the payment was successfully completed in this time + /// + /// # Arguments + /// + /// * `req` - the [PayOnchainRequest] containing: + /// * `address` - the Bitcoin address to pay to + /// * `prepare_response` - the [PreparePayOnchainResponse] from calling [BindingLiquidSdk::prepare_pay_onchain] + /// + /// # Errors + /// + /// * [PaymentError::PaymentTimeout] - if the payment could not be initiated in this time [Throws=PaymentError] SendPaymentResponse pay_onchain(PayOnchainRequest req); + /// Prepares to buy Bitcoin via a chain swap. + /// + /// # Arguments + /// + /// * `req` - the [PrepareBuyBitcoinRequest] containing: + /// * `provider` - the [BuyBitcoinProvider] to use + /// * `amount_sat` - the amount in satoshis to buy from the provider [Throws=PaymentError] PrepareBuyBitcoinResponse prepare_buy_bitcoin(PrepareBuyBitcoinRequest req); + /// Generate a URL to a third party provider used to buy Bitcoin via a chain swap. + /// + /// # Arguments + /// + /// * `req` - the [BuyBitcoinRequest] containing: + /// * `prepare_response` - the [PrepareBuyBitcoinResponse] from calling [BindingLiquidSdk::prepare_buy_bitcoin] + /// * `redirect_url` - the optional redirect URL the provider should redirect to after purchase [Throws=PaymentError] string buy_bitcoin(BuyBitcoinRequest req); + /// Lists the SDK payments in reverse chronological order, from newest to oldest. + /// The payments are determined based on onchain transactions and swaps. [Throws=PaymentError] sequence list_payments(ListPaymentsRequest req); + /// List all failed chain swaps that need to be refunded. + /// They can be refunded by calling [BindingLiquidSdk::prepare_refund] then [BindingLiquidSdk::refund]. [Throws=SdkError] sequence list_refundables(); + /// Prepares to refund a failed chain swap by calculating the refund transaction size and absolute fee. + /// + /// # Arguments + /// + /// * `req` - the [PrepareRefundRequest] containing: + /// * `swap_address` - the swap address to refund from [RefundableSwap::swap_address] + /// * `refund_address` - the Bitcoin address to refund to + /// * `fee_rate_sat_per_vbyte` - the fee rate at which to broadcast the refund transaction [Throws=SdkError] PrepareRefundResponse prepare_refund(PrepareRefundRequest req); + /// Refund a failed chain swap. + /// + /// # Arguments + /// + /// * `req` - the [RefundRequest] containing: + /// * `swap_address` - the swap address to refund from [RefundableSwap::swap_address] + /// * `refund_address` - the Bitcoin address to refund to + /// * `fee_rate_sat_per_vbyte` - the fee rate at which to broadcast the refund transaction [Throws=PaymentError] RefundResponse refund(RefundRequest req); + /// Rescans all expired chain swaps created from calling [BindingLiquidSdk::receive_onchain] within + /// the monitoring period to check if there are any confirmed funds available to refund. [Throws=SdkError] void rescan_onchain_swaps(); + /// Synchronizes the local state with the mempool and onchain data. [Throws=SdkError] void sync(); + /// Get the recommended Bitcoin fees based on the configured mempool.space instance. [Throws=SdkError] RecommendedFees recommended_fees(); + /// Backup the local state to the provided backup path. + /// + /// # Arguments + /// + /// * `req` - the [BackupRequest] containing: + /// * `backup_path` - the optional backup path. Defaults to [Config::working_dir] [Throws=SdkError] void backup(BackupRequest req); + /// Restores the local state from the provided backup path. + /// + /// # Arguments + /// + /// * `req` - the [RestoreRequest] containing: + /// * `backup_path` - the optional backup path. Defaults to [Config::working_dir] [Throws=SdkError] void restore(RestoreRequest req); + /// Disconnects the [BindingLiquidSdk] instance and stops the background tasks. [Throws=SdkError] void disconnect(); + /// Second step of LNURL-pay. The first step is [parse], which also validates the LNURL destination + /// and generates the [LnUrlPayRequest] payload needed here. + /// + /// This call will validate the `amount_msat` and `comment` parameters of `req` against the parameters + /// of the [LnUrlPayRequestData::req_data]. If they match the endpoint requirements, the LNURL payment + /// is made. [Throws=LnUrlPayError] LnUrlPayResult lnurl_pay(LnUrlPayRequest req); + /// Second step of LNURL-withdraw. The first step is [parse], which also validates the LNURL destination + /// and generates the [LnUrlWithdrawRequest] payload needed here. + /// + /// This call will validate the given `amount_msat` against the parameters + /// of the [LnUrlWithdrawRequestData::data]. If they match the endpoint requirements, the LNURL withdraw + /// request is made. A successful result here means the endpoint started the payment. [Throws=LnUrlWithdrawError] LnUrlWithdrawResult lnurl_withdraw(LnUrlWithdrawRequest req); + /// Third and last step of LNURL-auth. The first step is [parse], which also validates the LNURL destination + /// and generates the [LnUrlAuthRequestData] payload needed here. The second step is user approval of auth action. + /// + /// This call will sign the [LnUrlAuthRequestData::k1] of the `req_data` using the derived linking private key and DER-encodes the signature. + /// If they match the endpoint requirements, the LNURL auth request is made. A successful result here means the client signature is verified. [Throws=LnUrlAuthError] LnUrlCallbackStatus lnurl_auth(LnUrlAuthRequestData req_data); + /// Register for webhook callbacks at the given `webhook_url`. Each created swap after registering the + /// webhook will include the `webhook_url`. + /// + /// This method should be called every time the application is started and when the `webhook_url` changes. + /// For example, if the `webhook_url` contains a push notification token and the token changes after + /// the application was started, then this method should be called to register for callbacks at + /// the new correct `webhook_url`. To unregister a webhook call [BindingLiquidSdk::unregister_webhook]. [Throws=SdkError] void register_webhook(string webhook_url); + /// Unregister webhook callbacks. Each swap already created will continue to use the registered + /// `webhook_url` until complete. + /// + /// This can be called when callbacks are no longer needed or the `webhook_url` + /// has changed such that it needs unregistering. For example, the token is valid but the locale changes. + /// To register a webhook call [BindingLiquidSdk::register_webhook]. [Throws=SdkError] void unregister_webhook(); + /// Fetch live rates of fiat currencies, sorted by name. [Throws=SdkError] sequence fetch_fiat_rates(); + /// List all supported fiat currencies for which there is a known exchange rate. + /// List is sorted by the canonical name of the currency. [Throws=SdkError] sequence list_fiat_currencies(); }; diff --git a/lib/bindings/uniffi-bindgen.rs b/lib/bindings/uniffi-bindgen.rs index 9172dd706..af616d6cc 100644 --- a/lib/bindings/uniffi-bindgen.rs +++ b/lib/bindings/uniffi-bindgen.rs @@ -7,7 +7,7 @@ fn main() { let udl_file = "./src/breez_sdk_liquid.udl"; let out_dir = Utf8Path::new("ffi/kmp"); let config = Utf8Path::new("uniffi.toml"); - uniffi_bindgen::generate_external_bindings( + uniffi_bindgen_kmp::generate_external_bindings( KotlinBindingGenerator {}, udl_file, Some(config), diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 18a56d9d0..eb38c8796 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -248,7 +248,7 @@ pub struct ReceivePaymentRequest { #[derive(Debug, Serialize)] pub struct ReceivePaymentResponse { /// Either a BIP21 URI (Liquid or Bitcoin), a Liquid address - /// or an invoice, depending on the [PrepareReceivePaymentResponse] parameters + /// or an invoice, depending on the [PrepareReceiveResponse] parameters pub destination: String, } diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 2a02d64eb..783f5b443 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -944,7 +944,7 @@ class ReceivePaymentRequest { /// Returned when calling [crate::sdk::LiquidSdk::receive_payment]. class ReceivePaymentResponse { /// Either a BIP21 URI (Liquid or Bitcoin), a Liquid address - /// or an invoice, depending on the [PrepareReceivePaymentResponse] parameters + /// or an invoice, depending on the [PrepareReceiveResponse] parameters final String destination; const ReceivePaymentResponse({ diff --git a/packages/flutter/example/pubspec.lock b/packages/flutter/example/pubspec.lock new file mode 100644 index 000000000..6b7c15b00 --- /dev/null +++ b/packages/flutter/example/pubspec.lock @@ -0,0 +1,576 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + bip39: + dependency: "direct main" + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + breez_liquid: + dependency: "direct overridden" + description: + path: "../../dart" + relative: true + source: path + version: "0.3.4" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + url: "https://pub.dev" + source: hosted + version: "3.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + ffigen: + dependency: transitive + description: + name: ffigen + sha256: dead012f29db2be71ea152458f5eab600de98fbc244e01088ae6bf2616bceca7 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_breez_liquid: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.3.4" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_rust_bridge: + dependency: "direct main" + description: + name: flutter_rust_bridge + sha256: b0271cc147d5afccf9774809e4eef52b7357babe1a1a31db649df6f02dd27580 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + mobile_scanner: + dependency: "direct main" + description: + name: mobile_scanner + sha256: d234581c090526676fd8fab4ada92f35c6746e3fb4f05a399665d75a399fb760 + url: "https://pub.dev" + source: hosted + version: "5.2.3" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" + source: hosted + version: "2.1.4" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + url: "https://pub.dev" + source: hosted + version: "2.2.10" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + rxdart: + dependency: "direct main" + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + win32: + dependency: transitive + description: + name: win32 + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + url: "https://pub.dev" + source: hosted + version: "5.5.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f + url: "https://pub.dev" + source: hosted + version: "2.2.1" +sdks: + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidModule.kt b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidModule.kt index 6f91f6023..7592f5469 100644 --- a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidModule.kt +++ b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidModule.kt @@ -160,13 +160,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun removeEventListener( - id: String, + fun backup( + req: ReadableMap, promise: Promise, ) { executor.execute { try { - getBindingLiquidSdk().removeEventListener(id) + val backupRequest = + asBackupRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } + getBindingLiquidSdk().backup(backupRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -175,11 +177,16 @@ class BreezSDKLiquidModule( } @ReactMethod - fun getInfo(promise: Promise) { + fun buyBitcoin( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val res = getBindingLiquidSdk().getInfo() - promise.resolve(readableMapOf(res)) + val buyBitcoinRequest = + asBuyBitcoinRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "BuyBitcoinRequest")) } + val res = getBindingLiquidSdk().buyBitcoin(buyBitcoinRequest) + promise.resolve(res) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -187,15 +194,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun signMessage( + fun checkMessage( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val signMessageRequest = - asSignMessageRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "SignMessageRequest")) } - val res = getBindingLiquidSdk().signMessage(signMessageRequest) + val checkMessageRequest = + asCheckMessageRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "CheckMessageRequest")) } + val res = getBindingLiquidSdk().checkMessage(checkMessageRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -204,16 +211,12 @@ class BreezSDKLiquidModule( } @ReactMethod - fun checkMessage( - req: ReadableMap, - promise: Promise, - ) { + fun disconnect(promise: Promise) { executor.execute { try { - val checkMessageRequest = - asCheckMessageRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "CheckMessageRequest")) } - val res = getBindingLiquidSdk().checkMessage(checkMessageRequest) - promise.resolve(readableMapOf(res)) + getBindingLiquidSdk().disconnect() + bindingLiquidSdk = null + promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -221,15 +224,22 @@ class BreezSDKLiquidModule( } @ReactMethod - fun prepareSendPayment( - req: ReadableMap, - promise: Promise, - ) { + fun fetchFiatRates(promise: Promise) { executor.execute { try { - val prepareSendRequest = - asPrepareSendRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) } - val res = getBindingLiquidSdk().prepareSendPayment(prepareSendRequest) + val res = getBindingLiquidSdk().fetchFiatRates() + promise.resolve(readableArrayOf(res)) + } catch (e: Exception) { + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } + + @ReactMethod + fun fetchLightningLimits(promise: Promise) { + executor.execute { + try { + val res = getBindingLiquidSdk().fetchLightningLimits() promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -238,15 +248,10 @@ class BreezSDKLiquidModule( } @ReactMethod - fun sendPayment( - req: ReadableMap, - promise: Promise, - ) { + fun fetchOnchainLimits(promise: Promise) { executor.execute { try { - val sendPaymentRequest = - asSendPaymentRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "SendPaymentRequest")) } - val res = getBindingLiquidSdk().sendPayment(sendPaymentRequest) + val res = getBindingLiquidSdk().fetchOnchainLimits() promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -255,16 +260,10 @@ class BreezSDKLiquidModule( } @ReactMethod - fun prepareReceivePayment( - req: ReadableMap, - promise: Promise, - ) { + fun getInfo(promise: Promise) { executor.execute { try { - val prepareReceiveRequest = - asPrepareReceiveRequest(req) - ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) } - val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceiveRequest) + val res = getBindingLiquidSdk().getInfo() promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -273,17 +272,28 @@ class BreezSDKLiquidModule( } @ReactMethod - fun receivePayment( + fun listFiatCurrencies(promise: Promise) { + executor.execute { + try { + val res = getBindingLiquidSdk().listFiatCurrencies() + promise.resolve(readableArrayOf(res)) + } catch (e: Exception) { + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } + + @ReactMethod + fun listPayments( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val receivePaymentRequest = - asReceivePaymentRequest(req) - ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "ReceivePaymentRequest")) } - val res = getBindingLiquidSdk().receivePayment(receivePaymentRequest) - promise.resolve(readableMapOf(res)) + val listPaymentsRequest = + asListPaymentsRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "ListPaymentsRequest")) } + val res = getBindingLiquidSdk().listPayments(listPaymentsRequest) + promise.resolve(readableArrayOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -291,10 +301,28 @@ class BreezSDKLiquidModule( } @ReactMethod - fun fetchLightningLimits(promise: Promise) { + fun listRefundables(promise: Promise) { executor.execute { try { - val res = getBindingLiquidSdk().fetchLightningLimits() + val res = getBindingLiquidSdk().listRefundables() + promise.resolve(readableArrayOf(res)) + } catch (e: Exception) { + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } + + @ReactMethod + fun lnurlAuth( + reqData: ReadableMap, + promise: Promise, + ) { + executor.execute { + try { + val lnUrlAuthRequestData = + asLnUrlAuthRequestData(reqData) + ?: run { throw SdkException.Generic(errMissingMandatoryField("reqData", "LnUrlAuthRequestData")) } + val res = getBindingLiquidSdk().lnurlAuth(lnUrlAuthRequestData) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -303,10 +331,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun fetchOnchainLimits(promise: Promise) { + fun lnurlPay( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val res = getBindingLiquidSdk().fetchOnchainLimits() + val lnUrlPayRequest = + asLnUrlPayRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "LnUrlPayRequest")) } + val res = getBindingLiquidSdk().lnurlPay(lnUrlPayRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -315,16 +348,16 @@ class BreezSDKLiquidModule( } @ReactMethod - fun preparePayOnchain( + fun lnurlWithdraw( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val preparePayOnchainRequest = - asPreparePayOnchainRequest(req) - ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PreparePayOnchainRequest")) } - val res = getBindingLiquidSdk().preparePayOnchain(preparePayOnchainRequest) + val lnUrlWithdrawRequest = + asLnUrlWithdrawRequest(req) + ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "LnUrlWithdrawRequest")) } + val res = getBindingLiquidSdk().lnurlWithdraw(lnUrlWithdrawRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -368,16 +401,17 @@ class BreezSDKLiquidModule( } @ReactMethod - fun buyBitcoin( + fun preparePayOnchain( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val buyBitcoinRequest = - asBuyBitcoinRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "BuyBitcoinRequest")) } - val res = getBindingLiquidSdk().buyBitcoin(buyBitcoinRequest) - promise.resolve(res) + val preparePayOnchainRequest = + asPreparePayOnchainRequest(req) + ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PreparePayOnchainRequest")) } + val res = getBindingLiquidSdk().preparePayOnchain(preparePayOnchainRequest) + promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -385,28 +419,17 @@ class BreezSDKLiquidModule( } @ReactMethod - fun listPayments( + fun prepareReceivePayment( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val listPaymentsRequest = - asListPaymentsRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "ListPaymentsRequest")) } - val res = getBindingLiquidSdk().listPayments(listPaymentsRequest) - promise.resolve(readableArrayOf(res)) - } catch (e: Exception) { - promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) - } - } - } - - @ReactMethod - fun listRefundables(promise: Promise) { - executor.execute { - try { - val res = getBindingLiquidSdk().listRefundables() - promise.resolve(readableArrayOf(res)) + val prepareReceiveRequest = + asPrepareReceiveRequest(req) + ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) } + val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceiveRequest) + promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -432,15 +455,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun refund( + fun prepareSendPayment( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val refundRequest = - asRefundRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "RefundRequest")) } - val res = getBindingLiquidSdk().refund(refundRequest) + val prepareSendRequest = + asPrepareSendRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) } + val res = getBindingLiquidSdk().prepareSendPayment(prepareSendRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -449,11 +472,17 @@ class BreezSDKLiquidModule( } @ReactMethod - fun rescanOnchainSwaps(promise: Promise) { + fun receivePayment( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - getBindingLiquidSdk().rescanOnchainSwaps() - promise.resolve(readableMapOf("status" to "ok")) + val receivePaymentRequest = + asReceivePaymentRequest(req) + ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "ReceivePaymentRequest")) } + val res = getBindingLiquidSdk().receivePayment(receivePaymentRequest) + promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -461,11 +490,11 @@ class BreezSDKLiquidModule( } @ReactMethod - fun sync(promise: Promise) { + fun recommendedFees(promise: Promise) { executor.execute { try { - getBindingLiquidSdk().sync() - promise.resolve(readableMapOf("status" to "ok")) + val res = getBindingLiquidSdk().recommendedFees() + promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -473,10 +502,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun recommendedFees(promise: Promise) { + fun refund( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val res = getBindingLiquidSdk().recommendedFees() + val refundRequest = + asRefundRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "RefundRequest")) } + val res = getBindingLiquidSdk().refund(refundRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -485,15 +519,13 @@ class BreezSDKLiquidModule( } @ReactMethod - fun backup( - req: ReadableMap, + fun registerWebhook( + webhookUrl: String, promise: Promise, ) { executor.execute { try { - val backupRequest = - asBackupRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } - getBindingLiquidSdk().backup(backupRequest) + getBindingLiquidSdk().registerWebhook(webhookUrl) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -502,15 +534,13 @@ class BreezSDKLiquidModule( } @ReactMethod - fun restore( - req: ReadableMap, + fun removeEventListener( + id: String, promise: Promise, ) { executor.execute { try { - val restoreRequest = - asRestoreRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } - getBindingLiquidSdk().restore(restoreRequest) + getBindingLiquidSdk().removeEventListener(id) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -519,11 +549,10 @@ class BreezSDKLiquidModule( } @ReactMethod - fun disconnect(promise: Promise) { + fun rescanOnchainSwaps(promise: Promise) { executor.execute { try { - getBindingLiquidSdk().disconnect() - bindingLiquidSdk = null + getBindingLiquidSdk().rescanOnchainSwaps() promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -532,16 +561,16 @@ class BreezSDKLiquidModule( } @ReactMethod - fun lnurlPay( + fun restore( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val lnUrlPayRequest = - asLnUrlPayRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "LnUrlPayRequest")) } - val res = getBindingLiquidSdk().lnurlPay(lnUrlPayRequest) - promise.resolve(readableMapOf(res)) + val restoreRequest = + asRestoreRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } + getBindingLiquidSdk().restore(restoreRequest) + promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) } @@ -549,16 +578,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun lnurlWithdraw( + fun sendPayment( req: ReadableMap, promise: Promise, ) { executor.execute { try { - val lnUrlWithdrawRequest = - asLnUrlWithdrawRequest(req) - ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "LnUrlWithdrawRequest")) } - val res = getBindingLiquidSdk().lnurlWithdraw(lnUrlWithdrawRequest) + val sendPaymentRequest = + asSendPaymentRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "SendPaymentRequest")) } + val res = getBindingLiquidSdk().sendPayment(sendPaymentRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -567,16 +595,15 @@ class BreezSDKLiquidModule( } @ReactMethod - fun lnurlAuth( - reqData: ReadableMap, + fun signMessage( + req: ReadableMap, promise: Promise, ) { executor.execute { try { - val lnUrlAuthRequestData = - asLnUrlAuthRequestData(reqData) - ?: run { throw SdkException.Generic(errMissingMandatoryField("reqData", "LnUrlAuthRequestData")) } - val res = getBindingLiquidSdk().lnurlAuth(lnUrlAuthRequestData) + val signMessageRequest = + asSignMessageRequest(req) ?: run { throw SdkException.Generic(errMissingMandatoryField("req", "SignMessageRequest")) } + val res = getBindingLiquidSdk().signMessage(signMessageRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -585,13 +612,10 @@ class BreezSDKLiquidModule( } @ReactMethod - fun registerWebhook( - webhookUrl: String, - promise: Promise, - ) { + fun sync(promise: Promise) { executor.execute { try { - getBindingLiquidSdk().registerWebhook(webhookUrl) + getBindingLiquidSdk().sync() promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) @@ -610,28 +634,4 @@ class BreezSDKLiquidModule( } } } - - @ReactMethod - fun fetchFiatRates(promise: Promise) { - executor.execute { - try { - val res = getBindingLiquidSdk().fetchFiatRates() - promise.resolve(readableArrayOf(res)) - } catch (e: Exception) { - promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) - } - } - } - - @ReactMethod - fun listFiatCurrencies(promise: Promise) { - executor.execute { - try { - val res = getBindingLiquidSdk().listFiatCurrencies() - promise.resolve(readableArrayOf(res)) - } catch (e: Exception) { - promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) - } - } - } } diff --git a/packages/react-native/ios/RNBreezSDKLiquid.m b/packages/react-native/ios/RNBreezSDKLiquid.m index 330fb15ba..283f1d91b 100644 --- a/packages/react-native/ios/RNBreezSDKLiquid.m +++ b/packages/react-native/ios/RNBreezSDKLiquid.m @@ -38,177 +38,177 @@ @interface RCT_EXTERN_MODULE(RNBreezSDKLiquid, RCTEventEmitter) ) RCT_EXTERN_METHOD( - removeEventListener: (NSString*)id + backup: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - getInfo: (RCTPromiseResolveBlock)resolve + buyBitcoin: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - signMessage: (NSDictionary*)req + checkMessage: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - checkMessage: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + disconnect: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - prepareSendPayment: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + fetchFiatRates: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - sendPayment: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + fetchLightningLimits: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - prepareReceivePayment: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + fetchOnchainLimits: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - receivePayment: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + getInfo: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - fetchLightningLimits: (RCTPromiseResolveBlock)resolve + listFiatCurrencies: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - fetchOnchainLimits: (RCTPromiseResolveBlock)resolve + listPayments: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - preparePayOnchain: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + listRefundables: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - payOnchain: (NSDictionary*)req + lnurlAuth: (NSDictionary*)reqData resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - prepareBuyBitcoin: (NSDictionary*)req + lnurlPay: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - buyBitcoin: (NSDictionary*)req + lnurlWithdraw: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - listPayments: (NSDictionary*)req + payOnchain: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - listRefundables: (RCTPromiseResolveBlock)resolve + prepareBuyBitcoin: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - prepareRefund: (NSDictionary*)req + preparePayOnchain: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - refund: (NSDictionary*)req + prepareReceivePayment: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - rescanOnchainSwaps: (RCTPromiseResolveBlock)resolve + prepareRefund: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - sync: (RCTPromiseResolveBlock)resolve + prepareSendPayment: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - recommendedFees: (RCTPromiseResolveBlock)resolve + receivePayment: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - backup: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + recommendedFees: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - restore: (NSDictionary*)req + refund: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - disconnect: (RCTPromiseResolveBlock)resolve + registerWebhook: (NSString*)webhookUrl + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - lnurlPay: (NSDictionary*)req + removeEventListener: (NSString*)id resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - lnurlWithdraw: (NSDictionary*)req - resolve: (RCTPromiseResolveBlock)resolve + rescanOnchainSwaps: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - lnurlAuth: (NSDictionary*)reqData + restore: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - registerWebhook: (NSString*)webhookUrl + sendPayment: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - unregisterWebhook: (RCTPromiseResolveBlock)resolve + signMessage: (NSDictionary*)req + resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - fetchFiatRates: (RCTPromiseResolveBlock)resolve + sync: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD( - listFiatCurrencies: (RCTPromiseResolveBlock)resolve + unregisterWebhook: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) diff --git a/packages/react-native/ios/RNBreezSDKLiquid.swift b/packages/react-native/ios/RNBreezSDKLiquid.swift index c512b1d34..b39b915d6 100644 --- a/packages/react-native/ios/RNBreezSDKLiquid.swift +++ b/packages/react-native/ios/RNBreezSDKLiquid.swift @@ -144,118 +144,149 @@ class RNBreezSDKLiquid: RCTEventEmitter { } } - @objc(removeEventListener:resolve:reject:) - func removeEventListener(_ id: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(backup:resolve:reject:) + func backup(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try getBindingLiquidSdk().removeEventListener(id: id) + let backupRequest = try BreezSDKLiquidMapper.asBackupRequest(backupRequest: req) + try getBindingLiquidSdk().backup(req: backupRequest) resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(getInfo:reject:) - func getInfo(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(buyBitcoin:resolve:reject:) + func buyBitcoin(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - var res = try getBindingLiquidSdk().getInfo() - resolve(BreezSDKLiquidMapper.dictionaryOf(getInfoResponse: res)) + let buyBitcoinRequest = try BreezSDKLiquidMapper.asBuyBitcoinRequest(buyBitcoinRequest: req) + var res = try getBindingLiquidSdk().buyBitcoin(req: buyBitcoinRequest) + resolve(res) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(signMessage:resolve:reject:) - func signMessage(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(checkMessage:resolve:reject:) + func checkMessage(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let signMessageRequest = try BreezSDKLiquidMapper.asSignMessageRequest(signMessageRequest: req) - var res = try getBindingLiquidSdk().signMessage(req: signMessageRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(signMessageResponse: res)) + let checkMessageRequest = try BreezSDKLiquidMapper.asCheckMessageRequest(checkMessageRequest: req) + var res = try getBindingLiquidSdk().checkMessage(req: checkMessageRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(checkMessageResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(checkMessage:resolve:reject:) - func checkMessage(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(disconnect:reject:) + func disconnect(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let checkMessageRequest = try BreezSDKLiquidMapper.asCheckMessageRequest(checkMessageRequest: req) - var res = try getBindingLiquidSdk().checkMessage(req: checkMessageRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(checkMessageResponse: res)) + try getBindingLiquidSdk().disconnect() + bindingLiquidSdk = nil + resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(prepareSendPayment:resolve:reject:) - func prepareSendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(fetchFiatRates:reject:) + func fetchFiatRates(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let prepareSendRequest = try BreezSDKLiquidMapper.asPrepareSendRequest(prepareSendRequest: req) - var res = try getBindingLiquidSdk().prepareSendPayment(req: prepareSendRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(prepareSendResponse: res)) + var res = try getBindingLiquidSdk().fetchFiatRates() + resolve(BreezSDKLiquidMapper.arrayOf(rateList: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(sendPayment:resolve:reject:) - func sendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(fetchLightningLimits:reject:) + func fetchLightningLimits(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let sendPaymentRequest = try BreezSDKLiquidMapper.asSendPaymentRequest(sendPaymentRequest: req) - var res = try getBindingLiquidSdk().sendPayment(req: sendPaymentRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(sendPaymentResponse: res)) + var res = try getBindingLiquidSdk().fetchLightningLimits() + resolve(BreezSDKLiquidMapper.dictionaryOf(lightningPaymentLimitsResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(prepareReceivePayment:resolve:reject:) - func prepareReceivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(fetchOnchainLimits:reject:) + func fetchOnchainLimits(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let prepareReceiveRequest = try BreezSDKLiquidMapper.asPrepareReceiveRequest(prepareReceiveRequest: req) - var res = try getBindingLiquidSdk().prepareReceivePayment(req: prepareReceiveRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(prepareReceiveResponse: res)) + var res = try getBindingLiquidSdk().fetchOnchainLimits() + resolve(BreezSDKLiquidMapper.dictionaryOf(onchainPaymentLimitsResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(receivePayment:resolve:reject:) - func receivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(getInfo:reject:) + func getInfo(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let receivePaymentRequest = try BreezSDKLiquidMapper.asReceivePaymentRequest(receivePaymentRequest: req) - var res = try getBindingLiquidSdk().receivePayment(req: receivePaymentRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(receivePaymentResponse: res)) + var res = try getBindingLiquidSdk().getInfo() + resolve(BreezSDKLiquidMapper.dictionaryOf(getInfoResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(fetchLightningLimits:reject:) - func fetchLightningLimits(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(listFiatCurrencies:reject:) + func listFiatCurrencies(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - var res = try getBindingLiquidSdk().fetchLightningLimits() - resolve(BreezSDKLiquidMapper.dictionaryOf(lightningPaymentLimitsResponse: res)) + var res = try getBindingLiquidSdk().listFiatCurrencies() + resolve(BreezSDKLiquidMapper.arrayOf(fiatCurrencyList: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(fetchOnchainLimits:reject:) - func fetchOnchainLimits(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(listPayments:resolve:reject:) + func listPayments(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - var res = try getBindingLiquidSdk().fetchOnchainLimits() - resolve(BreezSDKLiquidMapper.dictionaryOf(onchainPaymentLimitsResponse: res)) + let listPaymentsRequest = try BreezSDKLiquidMapper.asListPaymentsRequest(listPaymentsRequest: req) + var res = try getBindingLiquidSdk().listPayments(req: listPaymentsRequest) + resolve(BreezSDKLiquidMapper.arrayOf(paymentList: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(preparePayOnchain:resolve:reject:) - func preparePayOnchain(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(listRefundables:reject:) + func listRefundables(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let preparePayOnchainRequest = try BreezSDKLiquidMapper.asPreparePayOnchainRequest(preparePayOnchainRequest: req) - var res = try getBindingLiquidSdk().preparePayOnchain(req: preparePayOnchainRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(preparePayOnchainResponse: res)) + var res = try getBindingLiquidSdk().listRefundables() + resolve(BreezSDKLiquidMapper.arrayOf(refundableSwapList: res)) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + + @objc(lnurlAuth:resolve:reject:) + func lnurlAuth(_ reqData: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + do { + let lnUrlAuthRequestData = try BreezSDKLiquidMapper.asLnUrlAuthRequestData(lnUrlAuthRequestData: reqData) + var res = try getBindingLiquidSdk().lnurlAuth(reqData: lnUrlAuthRequestData) + resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlCallbackStatus: res)) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + + @objc(lnurlPay:resolve:reject:) + func lnurlPay(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + do { + let lnUrlPayRequest = try BreezSDKLiquidMapper.asLnUrlPayRequest(lnUrlPayRequest: req) + var res = try getBindingLiquidSdk().lnurlPay(req: lnUrlPayRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlPayResult: res)) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + + @objc(lnurlWithdraw:resolve:reject:) + func lnurlWithdraw(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + do { + let lnUrlWithdrawRequest = try BreezSDKLiquidMapper.asLnUrlWithdrawRequest(lnUrlWithdrawRequest: req) + var res = try getBindingLiquidSdk().lnurlWithdraw(req: lnUrlWithdrawRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlWithdrawResult: res)) } catch let err { rejectErr(err: err, reject: reject) } @@ -283,33 +314,23 @@ class RNBreezSDKLiquid: RCTEventEmitter { } } - @objc(buyBitcoin:resolve:reject:) - func buyBitcoin(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - do { - let buyBitcoinRequest = try BreezSDKLiquidMapper.asBuyBitcoinRequest(buyBitcoinRequest: req) - var res = try getBindingLiquidSdk().buyBitcoin(req: buyBitcoinRequest) - resolve(res) - } catch let err { - rejectErr(err: err, reject: reject) - } - } - - @objc(listPayments:resolve:reject:) - func listPayments(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(preparePayOnchain:resolve:reject:) + func preparePayOnchain(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let listPaymentsRequest = try BreezSDKLiquidMapper.asListPaymentsRequest(listPaymentsRequest: req) - var res = try getBindingLiquidSdk().listPayments(req: listPaymentsRequest) - resolve(BreezSDKLiquidMapper.arrayOf(paymentList: res)) + let preparePayOnchainRequest = try BreezSDKLiquidMapper.asPreparePayOnchainRequest(preparePayOnchainRequest: req) + var res = try getBindingLiquidSdk().preparePayOnchain(req: preparePayOnchainRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(preparePayOnchainResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(listRefundables:reject:) - func listRefundables(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(prepareReceivePayment:resolve:reject:) + func prepareReceivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - var res = try getBindingLiquidSdk().listRefundables() - resolve(BreezSDKLiquidMapper.arrayOf(refundableSwapList: res)) + let prepareReceiveRequest = try BreezSDKLiquidMapper.asPrepareReceiveRequest(prepareReceiveRequest: req) + var res = try getBindingLiquidSdk().prepareReceivePayment(req: prepareReceiveRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(prepareReceiveResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } @@ -326,117 +347,116 @@ class RNBreezSDKLiquid: RCTEventEmitter { } } - @objc(refund:resolve:reject:) - func refund(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(prepareSendPayment:resolve:reject:) + func prepareSendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let refundRequest = try BreezSDKLiquidMapper.asRefundRequest(refundRequest: req) - var res = try getBindingLiquidSdk().refund(req: refundRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(refundResponse: res)) + let prepareSendRequest = try BreezSDKLiquidMapper.asPrepareSendRequest(prepareSendRequest: req) + var res = try getBindingLiquidSdk().prepareSendPayment(req: prepareSendRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(prepareSendResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(rescanOnchainSwaps:reject:) - func rescanOnchainSwaps(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(receivePayment:resolve:reject:) + func receivePayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try getBindingLiquidSdk().rescanOnchainSwaps() - resolve(["status": "ok"]) + let receivePaymentRequest = try BreezSDKLiquidMapper.asReceivePaymentRequest(receivePaymentRequest: req) + var res = try getBindingLiquidSdk().receivePayment(req: receivePaymentRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(receivePaymentResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(sync:reject:) - func sync(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(recommendedFees:reject:) + func recommendedFees(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try getBindingLiquidSdk().sync() - resolve(["status": "ok"]) + var res = try getBindingLiquidSdk().recommendedFees() + resolve(BreezSDKLiquidMapper.dictionaryOf(recommendedFees: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(recommendedFees:reject:) - func recommendedFees(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(refund:resolve:reject:) + func refund(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - var res = try getBindingLiquidSdk().recommendedFees() - resolve(BreezSDKLiquidMapper.dictionaryOf(recommendedFees: res)) + let refundRequest = try BreezSDKLiquidMapper.asRefundRequest(refundRequest: req) + var res = try getBindingLiquidSdk().refund(req: refundRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(refundResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(backup:resolve:reject:) - func backup(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(registerWebhook:resolve:reject:) + func registerWebhook(_ webhookUrl: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let backupRequest = try BreezSDKLiquidMapper.asBackupRequest(backupRequest: req) - try getBindingLiquidSdk().backup(req: backupRequest) + try getBindingLiquidSdk().registerWebhook(webhookUrl: webhookUrl) resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(restore:resolve:reject:) - func restore(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(removeEventListener:resolve:reject:) + func removeEventListener(_ id: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let restoreRequest = try BreezSDKLiquidMapper.asRestoreRequest(restoreRequest: req) - try getBindingLiquidSdk().restore(req: restoreRequest) + try getBindingLiquidSdk().removeEventListener(id: id) resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(disconnect:reject:) - func disconnect(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(rescanOnchainSwaps:reject:) + func rescanOnchainSwaps(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try getBindingLiquidSdk().disconnect() - bindingLiquidSdk = nil + try getBindingLiquidSdk().rescanOnchainSwaps() resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(lnurlPay:resolve:reject:) - func lnurlPay(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(restore:resolve:reject:) + func restore(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let lnUrlPayRequest = try BreezSDKLiquidMapper.asLnUrlPayRequest(lnUrlPayRequest: req) - var res = try getBindingLiquidSdk().lnurlPay(req: lnUrlPayRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlPayResult: res)) + let restoreRequest = try BreezSDKLiquidMapper.asRestoreRequest(restoreRequest: req) + try getBindingLiquidSdk().restore(req: restoreRequest) + resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(lnurlWithdraw:resolve:reject:) - func lnurlWithdraw(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(sendPayment:resolve:reject:) + func sendPayment(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let lnUrlWithdrawRequest = try BreezSDKLiquidMapper.asLnUrlWithdrawRequest(lnUrlWithdrawRequest: req) - var res = try getBindingLiquidSdk().lnurlWithdraw(req: lnUrlWithdrawRequest) - resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlWithdrawResult: res)) + let sendPaymentRequest = try BreezSDKLiquidMapper.asSendPaymentRequest(sendPaymentRequest: req) + var res = try getBindingLiquidSdk().sendPayment(req: sendPaymentRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(sendPaymentResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(lnurlAuth:resolve:reject:) - func lnurlAuth(_ reqData: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(signMessage:resolve:reject:) + func signMessage(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - let lnUrlAuthRequestData = try BreezSDKLiquidMapper.asLnUrlAuthRequestData(lnUrlAuthRequestData: reqData) - var res = try getBindingLiquidSdk().lnurlAuth(reqData: lnUrlAuthRequestData) - resolve(BreezSDKLiquidMapper.dictionaryOf(lnUrlCallbackStatus: res)) + let signMessageRequest = try BreezSDKLiquidMapper.asSignMessageRequest(signMessageRequest: req) + var res = try getBindingLiquidSdk().signMessage(req: signMessageRequest) + resolve(BreezSDKLiquidMapper.dictionaryOf(signMessageResponse: res)) } catch let err { rejectErr(err: err, reject: reject) } } - @objc(registerWebhook:resolve:reject:) - func registerWebhook(_ webhookUrl: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(sync:reject:) + func sync(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try getBindingLiquidSdk().registerWebhook(webhookUrl: webhookUrl) + try getBindingLiquidSdk().sync() resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) @@ -453,26 +473,6 @@ class RNBreezSDKLiquid: RCTEventEmitter { } } - @objc(fetchFiatRates:reject:) - func fetchFiatRates(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - do { - var res = try getBindingLiquidSdk().fetchFiatRates() - resolve(BreezSDKLiquidMapper.arrayOf(rateList: res)) - } catch let err { - rejectErr(err: err, reject: reject) - } - } - - @objc(listFiatCurrencies:reject:) - func listFiatCurrencies(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - do { - var res = try getBindingLiquidSdk().listFiatCurrencies() - resolve(BreezSDKLiquidMapper.arrayOf(fiatCurrencyList: res)) - } catch let err { - rejectErr(err: err, reject: reject) - } - } - func rejectErr(err: Error, reject: @escaping RCTPromiseRejectBlock) { var errorName = "Generic" var message = "\(err)" diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 4e63ba3ee..bd9a0e533 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -19,15 +19,36 @@ const BreezSDKLiquid = NativeModules.RNBreezSDKLiquid const BreezSDKLiquidEmitter = new NativeEventEmitter(BreezSDKLiquid) +/** + * Wrapper for the decrypted {@link AesSuccessActionDataResult.DECRYPTED} payload + */ export interface AesSuccessActionDataDecrypted { + /** + * Contents description, up to 144 characters + */ description: string + /** + * Decrypted content + */ plaintext: string } +/** + * An argument when calling {@link backup}. + */ export interface BackupRequest { + /** + * Path to the backup. + * + * If not set, it defaults to 'backup.sql' for mainnet and 'backup-testnet.sql' for testnet. + * The file will be saved in {@link ConnectRequest}'s `dataDir`. + */ backupPath?: string } +/** + * Wrapped in a {@link InputType.BITCOIN_ADDRESS}, this is the result of {@link parse} when given a plain or BIP-21 BTC address. + */ export interface BitcoinAddressData { address: string network: Network @@ -36,38 +57,95 @@ export interface BitcoinAddressData { message?: string } +/** + * An argument when calling {@link buyBitcoin}. + */ export interface BuyBitcoinRequest { prepareResponse: PrepareBuyBitcoinResponse + /** + * The optional URL to redirect to after completing the buy. + * + * For Moonpay, see + */ redirectUrl?: string } +/** + * An argument when calling {@link checkMessage}. + */ export interface CheckMessageRequest { + /** + * The message that was signed. + */ message: string + /** + * The public key of the node that signed the message. + */ pubkey: string + /** + * The zbase encoded signature to verify. + */ signature: string } +/** + * Returned when calling {@link checkMessage}. + */ export interface CheckMessageResponse { + /** + * Boolean value indicating whether the signature covers the message and + * was signed by the given pubkey. + */ isValid: boolean } +/** + * Configuration for the Liquid SDK + */ export interface Config { liquidElectrumUrl: string bitcoinElectrumUrl: string + /** + * The mempool.space API URL, has to be in the format: https://mempool.space/api + */ mempoolspaceUrl: string + /** + * Directory in which all SDK files (DB, log, cache) are stored. + * + * Prefix can be a relative or absolute path to this directory. + */ workingDir: string network: LiquidNetwork + /** + * Send payment timeout. See {@link sendPayment} + */ paymentTimeoutSec: number + /** + * Zero-conf minimum accepted fee-rate in millisatoshis per vbyte + */ zeroConfMinFeeRateMsat: number + /** + * Maximum amount in satoshi to accept zero-conf payments with + * Defaults to [crate::receive_swap::DEFAULT_ZERO_CONF_MAX_SAT] + */ zeroConfMaxAmountSat?: number + /** + * The Breez API key used for making requests to their mempool service + */ breezApiKey?: string } +/** + * An argument when calling {@link connect}. + */ export interface ConnectRequest { config: Config mnemonic: string } +/** + * Details about a supported currency in the fiat rate feed + */ export interface CurrencyInfo { name: string fractionSize: number @@ -83,13 +161,28 @@ export interface FiatCurrency { info: CurrencyInfo } +/** + * Returned when calling {@link getInfo}. + */ export interface GetInfoResponse { + /** + * Usable balance. This is the confirmed onchain balance minus `pendingSendSat`. + */ balanceSat: number + /** + * Amount that is being used for ongoing Send swaps + */ pendingSendSat: number + /** + * Incoming amount that is pending from ongoing Receive swaps + */ pendingReceiveSat: number pubkey: string } +/** + * Wrapper for a BOLT11 LN invoice + */ export interface LnInvoice { bolt11: string network: Network @@ -105,17 +198,32 @@ export interface LnInvoice { minFinalCltvExpiryDelta: number } +/** + * Returned when calling {@link fetchLightningLimits}. + */ export interface LightningPaymentLimitsResponse { + /** + * Amount limits for a Send Payment to be valid + */ send: Limits + /** + * Amount limits for a Receive Payment to be valid + */ receive: Limits } +/** + * The minimum and maximum in satoshis of a Lightning or onchain payment. + */ export interface Limits { minSat: number maxSat: number maxZeroConfSat: number } +/** + * Wrapped in a {@link InputType.LIQUID_ADDRESS}, this is the result of {@link parse} when given a plain or BIP-21 Liquid address. + */ export interface LiquidAddressData { address: string network: Network @@ -125,21 +233,54 @@ export interface LiquidAddressData { message?: string } +/** + * An argument when calling {@link listPayments}. + */ export interface ListPaymentsRequest { filters?: PaymentType[] + /** + * Epoch time, in seconds + */ fromTimestamp?: number + /** + * Epoch time, in seconds + */ toTimestamp?: number offset?: number limit?: number } +/** + * Wrapped in a {@link InputType.LN_URL_AUTH}, this is the result of {@link parse} when given a LNURL-auth endpoint. + * + * It represents the endpoint's parameters for the LNURL workflow. + * + * See + */ export interface LnUrlAuthRequestData { + /** + * Hex encoded 32 bytes of challenge + */ k1: string + /** + * Indicates the domain of the LNURL-auth service, to be shown to the user when asking for + * auth confirmation, as per LUD-04 spec. + */ domain: string + /** + * Indicates the URL of the LNURL-auth service, including the query arguments. This will be + * extended with the signed challenge and the linking key, then called in the second step of the workflow. + */ url: string + /** + * When available, one of: register, login, link, auth + */ action?: string } +/** + * Wrapped in a {@link InputType.LN_URL_ERROR}, this represents a LNURL-endpoint error. + */ export interface LnUrlErrorData { reason: string } @@ -149,23 +290,84 @@ export interface LnUrlPayErrorData { reason: string } +/** + * Represents a LNURL-pay request. + */ export interface LnUrlPayRequest { + /** + * The {@link LnUrlPayRequestData} returned by {@link parse} + */ data: LnUrlPayRequestData + /** + * The amount in millisatoshis for this payment + */ amountMsat: number + /** + * An optional comment for this payment + */ comment?: string + /** + * The external label or identifier of the {@link Payment} + */ paymentLabel?: string + /** + * Validates that, if there is a URL success action, the URL domain matches + * the LNURL callback domain. Defaults to `true` + */ validateSuccessActionUrl?: boolean } +/** + * Wrapped in a {@link InputType.LN_URL_PAY}, this is the result of {@link parse} when given a LNURL-pay endpoint. + * + * It represents the endpoint's parameters for the LNURL workflow. + * + * See + */ export interface LnUrlPayRequestData { callback: string + /** + * The minimum amount, in millisats, that this LNURL-pay endpoint accepts + */ minSendable: number + /** + * The maximum amount, in millisats, that this LNURL-pay endpoint accepts + */ maxSendable: number + /** + * As per LUD-06, `metadata` is a raw string (e.g. a json representation of the inner map) + */ metadataStr: string + /** + * The comment length accepted by this endpoint + * + * See + */ commentAllowed: number + /** + * Indicates the domain of the LNURL-pay service, to be shown to the user when asking for + * payment input, as per LUD-06 spec. + * + * Note: this is not the domain of the callback, but the domain of the LNURL-pay endpoint. + */ domain: string + /** + * Value indicating whether the recipient supports Nostr Zaps through NIP-57. + * + * See + */ allowsNostr: boolean + /** + * Optional recipient's lnurl provider's Nostr pubkey for NIP-57. If it exists it should be a + * valid BIP 340 public key in hex. + * + * See + * See + */ nostrPubkey?: string + /** + * If sending to a LN Address, this will be filled. + */ lnAddress?: string } @@ -175,16 +377,42 @@ export interface LnUrlPaySuccessData { } export interface LnUrlWithdrawRequest { + /** + * Request data containing information on how to call the lnurl withdraw + * endpoint. Typically retrieved by calling {@link parse} on a lnurl withdraw + * input. + */ data: LnUrlWithdrawRequestData + /** + * The amount to withdraw from the lnurl withdraw endpoint. Must be between + * `minWithdrawable` and `maxWithdrawable`. + */ amountMsat: number + /** + * Optional description that will be put in the payment request for the + * lnurl withdraw endpoint. + */ description?: string } +/** + * Wrapped in a {@link InputType.LN_URL_WITHDRAW}, this is the result of {@link parse} when given a LNURL-withdraw endpoint. + * + * It represents the endpoint's parameters for the LNURL workflow. + * + * See + */ export interface LnUrlWithdrawRequestData { callback: string k1: string defaultDescription: string + /** + * The minimum amount, in millisats, that this LNURL-withdraw endpoint accepts + */ minWithdrawable: number + /** + * The maximum amount, in millisats, that this LNURL-withdraw endpoint accepts + */ maxWithdrawable: number } @@ -192,12 +420,18 @@ export interface LnUrlWithdrawSuccessData { invoice: LnInvoice } +/** + * Locale-specific settings for the representation of a currency + */ export interface LocaleOverrides { locale: string spacing?: number symbol: SymbolType } +/** + * Localized name of a currency + */ export interface LocalizedName { locale: string name: string @@ -208,101 +442,238 @@ export interface LogEntry { level: string } +/** + * Wrapper for the {@link SuccessActionProcessed.MESSAGE} payload + */ export interface MessageSuccessActionData { message: string } +/** + * Returned when calling {@link fetchOnchainLimits}. + */ export interface OnchainPaymentLimitsResponse { + /** + * Amount limits for a Send Onchain Payment to be valid + */ send: Limits + /** + * Amount limits for a Receive Onchain Payment to be valid + */ receive: Limits } +/** + * An argument when calling {@link payOnchain}. + */ export interface PayOnchainRequest { address: string prepareResponse: PreparePayOnchainResponse } +/** + * Represents an SDK payment. + * + * By default, this is an onchain tx. It may represent a swap, if swap metadata is available. + */ export interface Payment { + /** + * Composite timestamp that can be used for sorting or displaying the payment. + * + * If this payment has an associated swap, it is the swap creation time. Otherwise, the point + * in time when the underlying tx was included in a block. If there is no associated swap + * available and the underlying tx is not yet confirmed, the value is 'now()'. + */ timestamp: number + /** + * The payment amount, which corresponds to the onchain tx amount. + * + * In case of an outbound payment (Send), this is the payer amount. Otherwise it's the receiver amount. + */ amountSat: number + /** + * Represents the fees paid by this wallet for this payment. + * + * ### Swaps + * If there is an associated Send Swap, these fees represent the total fees paid by this wallet + * (the sender). It is the difference between the amount that was sent and the amount received. + * + * If there is an associated Receive Swap, these fees represent the total fees paid by this wallet + * (the receiver). It is also the difference between the amount that was sent and the amount received. + * + * ### Pure onchain txs + * If no swap is associated with this payment: + * - for Send payments, this is the onchain tx fee + * - for Receive payments, this is zero + */ feesSat: number + /** + * If it is a {@link PaymentType.SEND} or {@link PaymentType.RECEIVE} payment + */ paymentType: PaymentType + /** + * Composite status representing the overall status of the payment. + * + * If the tx has no associated swap, this reflects the onchain tx status (confirmed or not). + * + * If the tx has an associated swap, this is determined by the swap status (pending or complete). + */ status: PaymentState + /** + * The details of a payment, depending on its {@link Payment.destination} `destination` and + * {@link Payment.paymentType} `type`. + */ details: PaymentDetails + /** + * The destination associated with the payment, if it was created via our SDK. + * Can be either a Liquid/Bitcoin address, a Liquid BIP21 URI or an invoice + */ destination?: string txId?: string } +/** + * An argument when calling {@link prepareBuyBitcoin}. + */ export interface PrepareBuyBitcoinRequest { provider: BuyBitcoinProvider amountSat: number } +/** + * Returned when calling {@link prepareBuyBitcoin}. + */ export interface PrepareBuyBitcoinResponse { provider: BuyBitcoinProvider amountSat: number feesSat: number } +/** + * An argument when calling {@link preparePayOnchain}. + */ export interface PreparePayOnchainRequest { amount: PayOnchainAmount + /** + * The optional fee rate of the Bitcoin claim transaction in sat/vB. Defaults to the swapper estimated claim fee. + */ feeRateSatPerVbyte?: number } +/** + * Returned when calling {@link preparePayOnchain}. + */ export interface PreparePayOnchainResponse { receiverAmountSat: number claimFeesSat: number totalFeesSat: number } +/** + * An argument when calling {@link prepareReceivePayment}. + */ export interface PrepareReceiveRequest { paymentMethod: PaymentMethod payerAmountSat?: number } +/** + * Returned when calling {@link prepareReceivePayment}. + */ export interface PrepareReceiveResponse { payerAmountSat?: number paymentMethod: PaymentMethod feesSat: number } +/** + * An argument when calling {@link prepareRefund}. + */ export interface PrepareRefundRequest { + /** + * The address where the swap funds are locked up + */ swapAddress: string + /** + * The address to refund the swap funds to + */ refundAddress: string + /** + * The fee rate in sat/vB for the refund transaction + */ feeRateSatPerVbyte: number } +/** + * Returned when calling {@link prepareRefund}. + */ export interface PrepareRefundResponse { txVsize: number txFeeSat: number refundTxId?: string } +/** + * An argument when calling {@link prepareSendPayment}. + */ export interface PrepareSendRequest { + /** + * The destination we intend to pay to. + * Supports BIP21 URIs, BOLT11 invoices and Liquid addresses + */ destination: string + /** + * Should only be set when paying directly onchain or to a BIP21 URI + * where no amount is specified + */ amountSat?: number } +/** + * Returned when calling {@link prepareSendPayment}. + */ export interface PrepareSendResponse { destination: SendDestination feesSat: number } +/** + * Denominator in an exchange rate + */ export interface Rate { coin: string value: number } +/** + * An argument when calling {@link receivePayment}. + */ export interface ReceivePaymentRequest { prepareResponse: PrepareReceiveResponse + /** + * The description for this payment request. + */ description?: string + /** + * If set to true, then the hash of the description will be used. + */ useDescriptionHash?: boolean } +/** + * Returned when calling {@link receivePayment}. + */ export interface ReceivePaymentResponse { + /** + * Either a BIP21 URI (Liquid or Bitcoin), a Liquid address + * or an invoice, depending on the {@link PrepareReceiveResponse} parameters + */ destination: string } +/** + * Returned when calling {@link recommendedFees}. + */ export interface RecommendedFees { fastestFee: number halfHourFee: number @@ -311,56 +682,116 @@ export interface RecommendedFees { minimumFee: number } +/** + * An argument when calling {@link refund}. + */ export interface RefundRequest { + /** + * The address where the swap funds are locked up + */ swapAddress: string + /** + * The address to refund the swap funds to + */ refundAddress: string + /** + * The fee rate in sat/vB for the refund transaction + */ feeRateSatPerVbyte: number } +/** + * Returned when calling {@link refund}. + */ export interface RefundResponse { refundTxId: string } +/** + * Returned when calling {@link listRefundables}. + */ export interface RefundableSwap { swapAddress: string timestamp: number amountSat: number } +/** + * An argument when calling {@link restore}. + */ export interface RestoreRequest { backupPath?: string } +/** + * A route hint for a LN payment + */ export interface RouteHint { hops: RouteHintHop[] } +/** + * Details of a specific hop in a larger route hint + */ export interface RouteHintHop { + /** + * The node id of the non-target end of the route + */ srcNodeId: string + /** + * The short channel id of this channel + */ shortChannelId: string + /** + * The fees which must be paid to use this channel + */ feesBaseMsat: number feesProportionalMillionths: number + /** + * The difference in CLTV values between this node and the next node + */ cltvExpiryDelta: number + /** + * The minimum value, in msat, which must be relayed to the next hop + */ htlcMinimumMsat?: number + /** + * The maximum value in msat available for routing with a single HTLC + */ htlcMaximumMsat?: number } +/** + * An argument when calling {@link sendPayment}. + */ export interface SendPaymentRequest { prepareResponse: PrepareSendResponse } +/** + * Returned when calling {@link sendPayment}. + */ export interface SendPaymentResponse { payment: Payment } +/** + * An argument when calling {@link signMessage}. + */ export interface SignMessageRequest { message: string } +/** + * Returned when calling {@link signMessage}. + */ export interface SignMessageResponse { signature: string } +/** + * Settings for the symbol representation of a currency + */ export interface SymbolType { grapheme?: string template?: string @@ -368,9 +799,23 @@ export interface SymbolType { position?: number } +/** + * Wrapper for the {@link SuccessActionProcessed.URL} payload + */ export interface UrlSuccessActionData { + /** + * Contents description, up to 144 characters + */ description: string + /** + * URL of the success action + */ url: string + /** + * Indicates the success URL domain matches the LNURL callback domain. + * + * See + */ matchesCallbackDomain: boolean } @@ -379,6 +824,9 @@ export enum AesSuccessActionDataResultVariant { ERROR_STATUS = "errorStatus" } +/** + * Result of decryption of {@link SuccessActionProcessed.AES} payload + */ export type AesSuccessActionDataResult = { type: AesSuccessActionDataResultVariant.DECRYPTED, data: AesSuccessActionDataDecrypted @@ -387,22 +835,70 @@ export type AesSuccessActionDataResult = { reason: string } +/** + * An argument of {@link PrepareBuyBitcoinRequest} when calling {@link prepareBuyBitcoin}. + */ export enum BuyBitcoinProvider { MOONPAY = "moonpay" } export enum InputTypeVariant { + /** + * # Supported standards + * + * - plain on-chain BTC address + * - BIP21 + */ BITCOIN_ADDRESS = "bitcoinAddress", + /** + * # Supported standards + * + * - plain on-chain liquid address + * - BIP21 on liquid/liquidtestnet + */ LIQUID_ADDRESS = "liquidAddress", BOLT11 = "bolt11", NODE_ID = "nodeId", URL = "url", + /** + * # Supported standards + * + * - LUD-01 LNURL bech32 encoding + * - LUD-06 `payRequest` spec + * - LUD-16 LN Address + * - LUD-17 Support for lnurlp prefix with non-bech32-encoded LNURL URLs + */ LN_URL_PAY = "lnUrlPay", + /** + * # Supported standards + * + * - LUD-01 LNURL bech32 encoding + * - LUD-03 `withdrawRequest` spec + * - LUD-17 Support for lnurlw prefix with non-bech32-encoded LNURL URLs + * + * # Not supported (yet) + * + * - LUD-14 `balanceCheck`: reusable `withdrawRequest`s + * - LUD-19 Pay link discoverable from withdraw link + */ LN_URL_WITHDRAW = "lnUrlWithdraw", + /** + * # Supported standards + * + * - LUD-01 LNURL bech32 encoding + * - LUD-04 `auth` base spec + * - LUD-17 Support for keyauth prefix with non-bech32-encoded LNURL URLs + */ LN_URL_AUTH = "lnUrlAuth", + /** + * Error returned by the LNURL endpoint + */ LN_URL_ERROR = "lnUrlError" } +/** + * Different kinds of inputs supported by {@link parse}, including any relevant details extracted from the input + */ export type InputType = { type: InputTypeVariant.BITCOIN_ADDRESS, address: BitcoinAddressData @@ -432,8 +928,18 @@ export type InputType = { data: LnUrlErrorData } +/** + * Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the + * Bitcoin network used. + */ export enum LiquidNetwork { + /** + * Mainnet Bitcoin and Liquid chains + */ MAINNET = "mainnet", + /** + * Testnet Bitcoin and Liquid chains + */ TESTNET = "testnet" } @@ -442,6 +948,17 @@ export enum LnUrlCallbackStatusVariant { ERROR_STATUS = "errorStatus" } +/** + * Contains the result of the entire LNURL interaction, as reported by the LNURL endpoint. + * + * * {@link LnUrlCallbackStatus.OK} indicates the interaction with the endpoint was valid, and the endpoint + * - started to pay the invoice asynchronously in the case of LNURL-withdraw, + * - verified the client signature in the case of LNURL-auth + * * {@link LnUrlCallbackStatus.ERROR_STATUS} indicates a generic issue the LNURL endpoint encountered, including a freetext + * description of the reason. + * + * Both cases are described in LUD-03 & LUD-04: + */ export type LnUrlCallbackStatus = { type: LnUrlCallbackStatusVariant.OK } | { @@ -455,6 +972,19 @@ export enum LnUrlPayResultVariant { PAY_ERROR = "payError" } +/** + * Contains the result of the entire LNURL-pay interaction, as reported by the LNURL endpoint. + * + * * {@link LnUrlPayResult.ENDPOINT_SUCCESS} indicates the payment is complete. The endpoint may return a {@link SuccessActionProcessed}, + * in which case, the wallet has to present it to the user as described in + * + * + * * {@link LnUrlPayResult.ENDPOINT_ERROR} indicates a generic issue the LNURL endpoint encountered, including a freetext + * field with the reason. + * + * * {@link LnUrlPayResult.PAY_ERROR} indicates that an error occurred while trying to pay the invoice from the LNURL endpoint. + * This includes the payment hash of the failed invoice and the failure reason. + */ export type LnUrlPayResult = { type: LnUrlPayResultVariant.ENDPOINT_SUCCESS, data: LnUrlPaySuccessData @@ -472,6 +1002,9 @@ export enum LnUrlWithdrawResultVariant { ERROR_STATUS = "errorStatus" } +/** + * {@link LnUrlCallbackStatus} specific to LNURL-withdraw, where the success case contains the invoice. + */ export type LnUrlWithdrawResult = { type: LnUrlWithdrawResultVariant.OK, data: LnUrlWithdrawSuccessData @@ -483,7 +1016,13 @@ export type LnUrlWithdrawResult = { data: LnUrlErrorData } +/** + * The different supported bitcoin networks + */ export enum Network { + /** + * Mainnet + */ BITCOIN = "bitcoin", TESTNET = "testnet", SIGNET = "signet", @@ -491,7 +1030,13 @@ export enum Network { } export enum PayOnchainAmountVariant { + /** + * The amount in satoshi that will be received + */ RECEIVER = "receiver", + /** + * Indicates that all available funds should be sent + */ DRAIN = "drain" } @@ -503,11 +1048,23 @@ export type PayOnchainAmount = { } export enum PaymentDetailsVariant { + /** + * Swapping to or from Lightning + */ LIGHTNING = "lightning", + /** + * Direct onchain payment to a Liquid address + */ LIQUID = "liquid", + /** + * Swapping to or from the Bitcoin chain + */ BITCOIN = "bitcoin" } +/** + * The specific details of a payment, depending on its type + */ export type PaymentDetails = { type: PaymentDetailsVariant.LIGHTNING, swapId: string @@ -528,19 +1085,87 @@ export type PaymentDetails = { refundTxAmountSat?: number } +/** + * The send/receive methods supported by the SDK + */ export enum PaymentMethod { LIGHTNING = "lightning", BITCOIN_ADDRESS = "bitcoinAddress", LIQUID_ADDRESS = "liquidAddress" } +/** + * The payment state of an individual payment. + */ export enum PaymentState { CREATED = "created", + /** + * ## Receive Swaps + * + * Covers the cases when + * - the lockup tx is seen in the mempool or + * - our claim tx is broadcast + * + * When the claim tx is broadcast, `claimTxId` is set in the swap. + * + * ## Send Swaps + * + * This is the status when our lockup tx was broadcast + * + * ## Chain Swaps + * + * This is the status when the user lockup tx was broadcast + * + * ## No swap data available + * + * If no associated swap is found, this indicates the underlying tx is not confirmed yet. + */ PENDING = "pending", + /** + * ## Receive Swaps + * + * Covers the case when the claim tx is confirmed. + * + * ## Send and Chain Swaps + * + * This is the status when the claim tx is broadcast and we see it in the mempool. + * + * ## No swap data available + * + * If no associated swap is found, this indicates the underlying tx is confirmed. + */ COMPLETE = "complete", + /** + * ## Receive Swaps + * + * This is the status when the swap failed for any reason and the Receive could not complete. + * + * ## Send and Chain Swaps + * + * This is the status when a swap refund was initiated and the refund tx is confirmed. + */ FAILED = "failed", + /** + * ## Send and Outgoing Chain Swaps + * + * This covers the case when the swap state is still Created and the swap fails to reach the + * Pending state in time. The TimedOut state indicates the lockup tx should never be broadcast. + */ TIMED_OUT = "timedOut", + /** + * ## Incoming Chain Swaps + * + * This covers the case when the swap failed for any reason and there is a user lockup tx. + * The swap in this case has to be manually refunded with a provided Bitcoin address + */ REFUNDABLE = "refundable", + /** + * ## Send and Chain Swaps + * + * This is the status when a refund was initiated and/or our refund tx was broadcast + * + * When the refund tx is broadcast, `refundTxId` is set in the swap. + */ REFUND_PENDING = "refundPending" } @@ -559,6 +1184,10 @@ export enum SdkEventVariant { SYNCED = "synced" } +/** + * Event emitted by the SDK. Add an {@link EventListener} by calling {@link addEventListener} + * to listen for emitted events. + */ export type SdkEvent = { type: SdkEventVariant.PAYMENT_FAILED, details: Payment @@ -586,6 +1215,9 @@ export enum SendDestinationVariant { BOLT11 = "bolt11" } +/** + * Specifies the supported destinations which can be payed by the SDK + */ export type SendDestination = { type: SendDestinationVariant.LIQUID_ADDRESS, addressData: LiquidAddressData @@ -611,15 +1243,39 @@ export type SuccessActionProcessed = { data: UrlSuccessActionData } +/** + * Interface that can be used to receive {@link SdkEvent}s emitted by the SDK. + */ export type EventListener = (e: SdkEvent) => void +/** + * Interface that can be used to receive {@link LogEntry}s emitted by the SDK. + */ export type Logger = (logEntry: LogEntry) => void +/** + * Initializes the SDK services and starts the background tasks. + * This must be called to create the {@link BindingLiquidSdk} instance. + * + * # Arguments + * + * * `req` - the {@link ConnectRequest} containing: + * * `mnemonic` - the Liquid wallet mnemonic + * * `config` - the SDK {@link Config} + */ export const connect = async (req: ConnectRequest): Promise => { const response = await BreezSDKLiquid.connect(req) return response } +/** + * Adds an event listener to the [LiquidSdk] instance, where all {@link SdkEvent}'s will be emitted to. + * The event listener can be removed be calling {@link removeEventListener}. + * + * # Arguments + * + * * `listener` - The listener which is an implementation of the {@link EventListener} trait + */ export const addEventListener = async (listener: EventListener): Promise => { const response = await BreezSDKLiquid.addEventListener() BreezSDKLiquidEmitter.addListener(`event-${response}`, listener) @@ -627,6 +1283,9 @@ export const addEventListener = async (listener: EventListener): Promise return response } +/** + * If used, this must be called before {@link connect}. + */ export const setLogger = async (logger: Logger): Promise => { const subscription = BreezSDKLiquidEmitter.addListener("breezSdkLiquidLog", logger) @@ -637,165 +1296,404 @@ export const setLogger = async (logger: Logger): Promise => return subscription } +/** + * Get the full default {@link Config} for specific {@link LiquidNetwork}. + */ export const defaultConfig = async (network: LiquidNetwork): Promise => { const response = await BreezSDKLiquid.defaultConfig(network) return response } +/** + * Parses a string into an {@link InputType}. + */ export const parse = async (input: string): Promise => { const response = await BreezSDKLiquid.parse(input) return response } +/** + * Parses a string into an {@link LnInvoice}. + */ export const parseInvoice = async (input: string): Promise => { const response = await BreezSDKLiquid.parseInvoice(input) return response } -export const removeEventListener = async (id: string): Promise => { - await BreezSDKLiquid.removeEventListener(id) +/** + * Backup the local state to the provided backup path. + * + * # Arguments + * + * * `req` - the {@link BackupRequest} containing: + * * `backupPath` - the optional backup path. Defaults to {@link Config.workingDir} + */ +export const backup = async (req: BackupRequest): Promise => { + await BreezSDKLiquid.backup(req) } -export const getInfo = async (): Promise => { - const response = await BreezSDKLiquid.getInfo() +/** + * Generate a URL to a third party provider used to buy Bitcoin via a chain swap. + * + * # Arguments + * + * * `req` - the {@link BuyBitcoinRequest} containing: + * * `prepareResponse` - the {@link PrepareBuyBitcoinResponse} from calling {@link prepareBuyBitcoin} + * * `redirectUrl` - the optional redirect URL the provider should redirect to after purchase + */ +export const buyBitcoin = async (req: BuyBitcoinRequest): Promise => { + const response = await BreezSDKLiquid.buyBitcoin(req) return response } -export const signMessage = async (req: SignMessageRequest): Promise => { - const response = await BreezSDKLiquid.signMessage(req) +/** + * Check whether given message was signed by the given + * pubkey and the signature (zbase encoded) is valid. + */ +export const checkMessage = async (req: CheckMessageRequest): Promise => { + const response = await BreezSDKLiquid.checkMessage(req) return response } -export const checkMessage = async (req: CheckMessageRequest): Promise => { - const response = await BreezSDKLiquid.checkMessage(req) +/** + * Disconnects the {@link BindingLiquidSdk} instance and stops the background tasks. + */ +export const disconnect = async (): Promise => { + await BreezSDKLiquid.disconnect() +} + +/** + * Fetch live rates of fiat currencies, sorted by name. + */ +export const fetchFiatRates = async (): Promise => { + const response = await BreezSDKLiquid.fetchFiatRates() return response } -export const prepareSendPayment = async (req: PrepareSendRequest): Promise => { - const response = await BreezSDKLiquid.prepareSendPayment(req) +/** + * Fetch the current payment limits for {@link sendPayment} and {@link receivePayment}. + */ +export const fetchLightningLimits = async (): Promise => { + const response = await BreezSDKLiquid.fetchLightningLimits() return response } -export const sendPayment = async (req: SendPaymentRequest): Promise => { - const response = await BreezSDKLiquid.sendPayment(req) +/** + * Fetch the current payment limits for {@link payOnchain} and {@link receiveOnchain}. + */ +export const fetchOnchainLimits = async (): Promise => { + const response = await BreezSDKLiquid.fetchOnchainLimits() return response } -export const prepareReceivePayment = async (req: PrepareReceiveRequest): Promise => { - const response = await BreezSDKLiquid.prepareReceivePayment(req) +/** + * Get the wallet info, calculating the current pending and confirmed balances. + */ +export const getInfo = async (): Promise => { + const response = await BreezSDKLiquid.getInfo() return response } -export const receivePayment = async (req: ReceivePaymentRequest): Promise => { - const response = await BreezSDKLiquid.receivePayment(req) +/** + * List all supported fiat currencies for which there is a known exchange rate. + * List is sorted by the canonical name of the currency. + */ +export const listFiatCurrencies = async (): Promise => { + const response = await BreezSDKLiquid.listFiatCurrencies() return response } -export const fetchLightningLimits = async (): Promise => { - const response = await BreezSDKLiquid.fetchLightningLimits() +/** + * Lists the SDK payments in reverse chronological order, from newest to oldest. + * The payments are determined based on onchain transactions and swaps. + */ +export const listPayments = async (req: ListPaymentsRequest): Promise => { + const response = await BreezSDKLiquid.listPayments(req) return response } -export const fetchOnchainLimits = async (): Promise => { - const response = await BreezSDKLiquid.fetchOnchainLimits() +/** + * List all failed chain swaps that need to be refunded. + * They can be refunded by calling {@link prepareRefund} then {@link refund}. + */ +export const listRefundables = async (): Promise => { + const response = await BreezSDKLiquid.listRefundables() return response } -export const preparePayOnchain = async (req: PreparePayOnchainRequest): Promise => { - const response = await BreezSDKLiquid.preparePayOnchain(req) +/** + * Third and last step of LNURL-auth. The first step is {@link parse}, which also validates the LNURL destination + * and generates the {@link LnUrlAuthRequestData} payload needed here. The second step is user approval of auth action. + * + * This call will sign the {@link LnUrlAuthRequestData.k1} of the `reqData` using the derived linking private key and DER-encodes the signature. + * If they match the endpoint requirements, the LNURL auth request is made. A successful result here means the client signature is verified. + */ +export const lnurlAuth = async (reqData: LnUrlAuthRequestData): Promise => { + const response = await BreezSDKLiquid.lnurlAuth(reqData) return response } +/** + * Second step of LNURL-pay. The first step is {@link parse}, which also validates the LNURL destination + * and generates the {@link LnUrlPayRequest} payload needed here. + * + * This call will validate the `amountMsat` and `comment` parameters of `req` against the parameters + * of the {@link LnUrlPayRequestData.reqData}. If they match the endpoint requirements, the LNURL payment + * is made. + */ +export const lnurlPay = async (req: LnUrlPayRequest): Promise => { + const response = await BreezSDKLiquid.lnurlPay(req) + return response +} + +/** + * Second step of LNURL-withdraw. The first step is {@link parse}, which also validates the LNURL destination + * and generates the {@link LnUrlWithdrawRequest} payload needed here. + * + * This call will validate the given `amountMsat` against the parameters + * of the {@link LnUrlWithdrawRequestData.data}. If they match the endpoint requirements, the LNURL withdraw + * request is made. A successful result here means the endpoint started the payment. + */ +export const lnurlWithdraw = async (req: LnUrlWithdrawRequest): Promise => { + const response = await BreezSDKLiquid.lnurlWithdraw(req) + return response +} + +/** + * Pays to a Bitcoin address via a chain swap. + * + * Depending on {@link Config}'s `paymentTimeoutSec`, this function will return: + * * {@link PaymentState.PENDING} payment - if the payment could be initiated but didn't yet + * complete in this time + * * {@link PaymentState.COMPLETE} payment - if the payment was successfully completed in this time + * + * # Arguments + * + * * `req` - the {@link PayOnchainRequest} containing: + * * `address` - the Bitcoin address to pay to + * * `prepareResponse` - the {@link PreparePayOnchainResponse} from calling {@link preparePayOnchain} + * + * # Errors + * + * * {@link PaymentError.PAYMENT_TIMEOUT} - if the payment could not be initiated in this time + */ export const payOnchain = async (req: PayOnchainRequest): Promise => { const response = await BreezSDKLiquid.payOnchain(req) return response } +/** + * Prepares to buy Bitcoin via a chain swap. + * + * # Arguments + * + * * `req` - the {@link PrepareBuyBitcoinRequest} containing: + * * `provider` - the {@link BuyBitcoinProvider} to use + * * `amountSat` - the amount in satoshis to buy from the provider + */ export const prepareBuyBitcoin = async (req: PrepareBuyBitcoinRequest): Promise => { const response = await BreezSDKLiquid.prepareBuyBitcoin(req) return response } -export const buyBitcoin = async (req: BuyBitcoinRequest): Promise => { - const response = await BreezSDKLiquid.buyBitcoin(req) - return response -} - -export const listPayments = async (req: ListPaymentsRequest): Promise => { - const response = await BreezSDKLiquid.listPayments(req) +/** + * Prepares to pay to a Bitcoin address via a chain swap. + * + * # Arguments + * + * * `req` - the {@link PreparePayOnchainRequest} containing: + * * `amount` - which can be of two types: {@link PayOnchainAmount.DRAIN}, which uses all funds, + * and {@link PayOnchainAmount.RECEIVER}, which sets the amount the receiver should receive + * * `feeRateSatPerVbyte` - the optional fee rate of the Bitcoin claim transaction. Defaults to the swapper estimated claim fee + */ +export const preparePayOnchain = async (req: PreparePayOnchainRequest): Promise => { + const response = await BreezSDKLiquid.preparePayOnchain(req) return response } -export const listRefundables = async (): Promise => { - const response = await BreezSDKLiquid.listRefundables() +/** + * Prepares to receive a Lightning payment via a reverse submarine swap. + * + * # Arguments + * + * * `req` - the {@link PrepareReceiveRequest} containing: + * * `payerAmountSat` - the amount in satoshis to be paid by the payer + * * `paymentMethod` - the supported payment methods; either an invoice, a Liquid address or a Bitcoin address + */ +export const prepareReceivePayment = async (req: PrepareReceiveRequest): Promise => { + const response = await BreezSDKLiquid.prepareReceivePayment(req) return response } +/** + * Prepares to refund a failed chain swap by calculating the refund transaction size and absolute fee. + * + * # Arguments + * + * * `req` - the {@link PrepareRefundRequest} containing: + * * `swapAddress` - the swap address to refund from {@link RefundableSwap.swapAddress} + * * `refundAddress` - the Bitcoin address to refund to + * * `feeRateSatPerVbyte` - the fee rate at which to broadcast the refund transaction + */ export const prepareRefund = async (req: PrepareRefundRequest): Promise => { const response = await BreezSDKLiquid.prepareRefund(req) return response } -export const refund = async (req: RefundRequest): Promise => { - const response = await BreezSDKLiquid.refund(req) +/** + * Prepares to pay a Lightning invoice via a submarine swap. + * + * # Arguments + * + * * `req` - the {@link PrepareSendRequest} containing: + * * `destination` - Either a Liquid BIP21 URI/address or a BOLT11 invoice + * * `amountSat` - Should only be specified when paying directly onchain or via amount-less BIP21 + * + * # Returns + * Returns a {@link PrepareSendResponse} containing: + * * `destination` - the parsed destination, of type {@link SendDestination} + * * `feesSat` - the additional fees which will be paid by the sender + */ +export const prepareSendPayment = async (req: PrepareSendRequest): Promise => { + const response = await BreezSDKLiquid.prepareSendPayment(req) return response } -export const rescanOnchainSwaps = async (): Promise => { - await BreezSDKLiquid.rescanOnchainSwaps() -} - -export const sync = async (): Promise => { - await BreezSDKLiquid.sync() +/** + * Receive a Lightning payment via a reverse submarine swap, a chain swap or via direct Liquid + * payment. + * + * # Arguments + * + * * `req` - the {@link ReceivePaymentRequest} containing: + * * `prepareResponse` - the {@link PrepareReceiveResponse} from calling {@link prepareReceivePayment} + * * `description` - the optional payment description + * * `useDescriptionHash` - optional if true uses the hash of the description + * + * # Returns + * + * * A {@link ReceivePaymentResponse} containing: + * * `destination` - the final destination to be paid by the payer, either a BIP21 URI (Liquid or Bitcoin), a Liquid address or an invoice + */ +export const receivePayment = async (req: ReceivePaymentRequest): Promise => { + const response = await BreezSDKLiquid.receivePayment(req) + return response } +/** + * Get the recommended Bitcoin fees based on the configured mempool.space instance. + */ export const recommendedFees = async (): Promise => { const response = await BreezSDKLiquid.recommendedFees() return response } -export const backup = async (req: BackupRequest): Promise => { - await BreezSDKLiquid.backup(req) +/** + * Refund a failed chain swap. + * + * # Arguments + * + * * `req` - the {@link RefundRequest} containing: + * * `swapAddress` - the swap address to refund from {@link RefundableSwap.swapAddress} + * * `refundAddress` - the Bitcoin address to refund to + * * `feeRateSatPerVbyte` - the fee rate at which to broadcast the refund transaction + */ +export const refund = async (req: RefundRequest): Promise => { + const response = await BreezSDKLiquid.refund(req) + return response } -export const restore = async (req: RestoreRequest): Promise => { - await BreezSDKLiquid.restore(req) +/** + * Register for webhook callbacks at the given `webhookUrl`. Each created swap after registering the + * webhook will include the `webhookUrl`. + * + * This method should be called every time the application is started and when the `webhookUrl` changes. + * For example, if the `webhookUrl` contains a push notification token and the token changes after + * the application was started, then this method should be called to register for callbacks at + * the new correct `webhookUrl`. To unregister a webhook call {@link unregisterWebhook}. + */ +export const registerWebhook = async (webhookUrl: string): Promise => { + await BreezSDKLiquid.registerWebhook(webhookUrl) } -export const disconnect = async (): Promise => { - await BreezSDKLiquid.disconnect() +/** + * Removes an event listener from the {@link BindingLiquidSdk} instance. + * + * # Arguments + * + * * `id` - the event listener id returned by {@link addEventListener} + */ +export const removeEventListener = async (id: string): Promise => { + await BreezSDKLiquid.removeEventListener(id) } -export const lnurlPay = async (req: LnUrlPayRequest): Promise => { - const response = await BreezSDKLiquid.lnurlPay(req) - return response +/** + * Rescans all expired chain swaps created from calling {@link receiveOnchain} within + * the monitoring period to check if there are any confirmed funds available to refund. + */ +export const rescanOnchainSwaps = async (): Promise => { + await BreezSDKLiquid.rescanOnchainSwaps() } -export const lnurlWithdraw = async (req: LnUrlWithdrawRequest): Promise => { - const response = await BreezSDKLiquid.lnurlWithdraw(req) +/** + * Restores the local state from the provided backup path. + * + * # Arguments + * + * * `req` - the {@link RestoreRequest} containing: + * * `backupPath` - the optional backup path. Defaults to {@link Config.workingDir} + */ +export const restore = async (req: RestoreRequest): Promise => { + await BreezSDKLiquid.restore(req) +} + +/** + * Either pays a Lightning invoice via a submarine swap or sends funds directly to an address. + * + * Depending on {@link Config}'s `paymentTimeoutSec`, this function will return: + * * {@link PaymentState.PENDING} payment - if the payment could be initiated but didn't yet + * complete in this time + * * {@link PaymentState.COMPLETE} payment - if the payment was successfully completed in this time + * + * # Arguments + * + * * `req` - A {@link SendPaymentRequest}, containing: + * * `prepareResponse` - the {@link PrepareSendResponse} returned by {@link prepareSendPayment} + * + * # Errors + * + * * {@link PaymentError.PAYMENT_TIMEOUT} - if the payment could not be initiated in this time + */ +export const sendPayment = async (req: SendPaymentRequest): Promise => { + const response = await BreezSDKLiquid.sendPayment(req) return response } -export const lnurlAuth = async (reqData: LnUrlAuthRequestData): Promise => { - const response = await BreezSDKLiquid.lnurlAuth(reqData) +/** + * Sign given message with the private key. Returns a zbase encoded signature. + */ +export const signMessage = async (req: SignMessageRequest): Promise => { + const response = await BreezSDKLiquid.signMessage(req) return response } -export const registerWebhook = async (webhookUrl: string): Promise => { - await BreezSDKLiquid.registerWebhook(webhookUrl) +/** + * Synchronizes the local state with the mempool and onchain data. + */ +export const sync = async (): Promise => { + await BreezSDKLiquid.sync() } +/** + * Unregister webhook callbacks. Each swap already created will continue to use the registered + * `webhookUrl` until complete. + * + * This can be called when callbacks are no longer needed or the `webhookUrl` + * has changed such that it needs unregistering. For example, the token is valid but the locale changes. + * To register a webhook call {@link registerWebhook}. + */ export const unregisterWebhook = async (): Promise => { await BreezSDKLiquid.unregisterWebhook() } -export const fetchFiatRates = async (): Promise => { - const response = await BreezSDKLiquid.fetchFiatRates() - return response -} - -export const listFiatCurrencies = async (): Promise => { - const response = await BreezSDKLiquid.listFiatCurrencies() - return response -}